Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the alternatives to public fields?

I am programming a game in java, and as the question title suggestions i am using public fields in my classes. (for the time being)

From what i have seen public fields are bad and i have some understanding why. (but if someone could clarify why you should not use them, that would be appreciated)

The thing is that also from what i have seen, (and it seems logical) is that using private fields, but using getters and setters to access them is also not good as it defeats the point of using private fields in the first place.

So, my question is, what are the alternatives? or do i really have to use private fields with getters and setters?

For reference here is one of my classes, and some of its methods.

I will elaborate more if needs be.

public double health;
//The player's fields.
public String name;
public double goldCount;
public double maxWeight;
public double currentWeight;
public double maxBackPckSlts;
public double usedBackPckSlts; // The current back pack slots in use
public double maxHealth; // Maximum amount of health
public ArrayList<String> backPack = new ArrayList<String>();

//This method happens when ever the player dynamically takes damage(i.e. when it is not scripted for the player to take damage.
//Parameters will be added to make it dynamic so the player can take any spread of damage.
public void beDamaged(double damage)
{
    this.health -= damage;
    if (this.health < 0)
    {
        this.health = 0;
    }
}

EDIT: For checking purposes, this is what my Weapon class looks like now: (Code sample is not working for some reason, so it does not look right.)

private final double DAMAGE;
private final double SPEED;

public Weapon(double initialDmg,double initialSpd,String startName,double initialWg)
{
    DAMAGE = initialDmg;
    SPEED = initialSpd;
    setItemName(startName);
    setItemWeight(initialWg);
}

public double getSpeed() 
{
    return SPEED;
}


public double getDamage()
{
    return DAMAGE;
}

As you can see, as the Weapon's DAMAGE and SPEED do not need to be changed, they can be final's for the time being. (if, later in the game, i decided these values can be "Upgraded" so to speak, i may add setters then , with validation, or just make a new weapon with the upgraded values) They get set in the Weapon's constructor.

Conclusion: getters and setters are fine, as long as they are used smartly, and only used when needed. (however)

like image 675
James Avatar asked Jan 01 '11 15:01

James


2 Answers

It's common to use getters and setters instead of giving other objects permission to change your fields directly. That might not make any sense when you see that 99.99% of your getters and setters don't do anything except what you could have done with direct access to the fields. But what happens when you decide that when a player is damaged beyond a point, he drops half his inventory? Or you want to restrict how many backpack slots can be used by magical items? You either have to hunt down all the places in your code where you modify the fields, or, if you used getters and setters, you make the changes entirely in the class. That's the heart of object oriented programming - that you've encapsulated "knowledge" of what an object does within the object itself, not spread it out among all the objects that interact with that object.

like image 165
Paul Tomblin Avatar answered Oct 16 '22 16:10

Paul Tomblin


One of the core concepts of object-oriented programming is encapsulation -- that is, hiding an object's state (for example, the data in the object) from the outside, and letting the object handle it's own state.

When encapsulation is done well, the object's state can only be affected from the outside world through the interfaces provided by the object, such as methods the object has.

I think your code is already starting to use encapsulation.

Let's take a look at the code

Let's take a look at the beDamaged method.

public void beDamaged(double damage)
{
    this.health -= damage;

    if (this.health < 0)
    {
        this.health = 0;
    }
}

Here's we can see that this method will be called by the outside world, and the player's health will be affected. It also contains logic, so the health cannot be a negative number. The player's beDamaged method that you wrote is keeping the state of the object within the parameters that you defined as being the valid state.

Let's infer something about the player

Now, from the above, I think I can infer the following about the player object:

A player's health cannot be a negative number.

Is what we inferred always true?

Let's see if this can always be true from the code you've provided.

Aha! We have a little problem here:

public double health;

With the health field being public, the outside world can directly manipulate the field in order to place the player object's state into one that is probably not desired, by some code like the following:

Player player = new Player();
player.health = -100

I'm going to guess that the player shouldn't be in a state where the health is a negative number.

What can we do about it?

How could that have been avoided? -- by having the health field private.

Now, the only way to affect the player's health would be through the beDamaged and gainHealth methods, and that's probably the right way for the outside world to affect your player's health.

Which also means this -- when you make a field private, that does not automatically mean that you should make getters and setters for the field.

Private fields does not necessitate getters and setters

Getters and setters are usually a way to directly affect a field that an object has, maybe with some validation to prevent bad input from making your object have a state that it shouldn't, but there are going to be times where the object itself should be in charge of affecting the data, rather than an outside entity.

like image 23
coobird Avatar answered Oct 16 '22 14:10

coobird