What are the alternatives to public fields?

拟墨画扇 提交于 2019-12-04 00:07:46

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.

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.

In Java, using private fields with getters/setters is the recommend practice, provided external clients of your class really need access to those fields.

Otherwise keep them as private fields and simply don't provide a getter/setter.

There are various reasons why this is a best practice:

  1. If clients are using your field directly and later something needs to change regarding that, you're stuck. With a getter you can do a whole lot of things before the field is accessed.
  2. There is something called the JavaBeans specification that requires you to use getter/setters. Without them your class (then called bean) won't interoperate with that. JSP and JSF's EL is one example of something that required your class to comply with JavaBeans standards.

(p.s. unrelated to your question, but you'd better not declare backPack as an ArrayList. Declare as List; code to interface, not to implementation)

If you have a private field with a method get() and a method set() that don't do anything other than retrieve and assign the value, you should just make the field public, as the field isn't really private, and the getters and setters only hurt performance. If the getters and setters check the value being set or if the value is allowed to retrieve, then go ahead and use getters and setters. e.g. If you have a variable private int width; and someone tries to put in -1 with a setter, and the setter makes sure it isn't negative, then that is a good use. For example:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

This would be a good use of getters and setters. Otherwise, they hurt your performance if the only thing they do is assign or get the value without anything else.

So to make a long story short:

Use getters and setters when doing anything other than retrieving or assigning a value. Else, just use public fields.

i.e.

BAD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    width = w;
}

GOOD:

private int width;
public int get(){
    return width;
}
public void set(int w){
    if (w < 0) throw new RuntimeException();
    else width = w;
}

GOOD if you don't want anything other than getting or setting:

public int width;

About this:

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.

The main problem is that many developers automatically generate getters and setters for all private fields. And if you're going to do that, I agree, you might as well keep the field public (no, public fields are even worse).

For every field that you have, you should check:

a) does it need a Getter (do other classes need to know the value of this field)
b) does it need a Setter (do other classes need to be able to change the value of this field)
c) or does the field need to be immutable (final), if so it must be initialized during definition or in the constructor (and it can obviously have no setter)

But you should hardly ever (exception: value objects) assume that all private fields will have getters and setters and let your IDE generate them all.

An advantage of using getters and especially setters is, that it is much easier to debug write access to the fields.

private fields and setters and getters is indeed your best way to go.

Further note that this is in general good code in any language as it keeps your security nice and tight while also giving you a structure that is far easier to debug and maintain. (Don't forget to document btw!)

All in all, go with setters and getters, it's just good practice even if you find options.

Getters and setters are part of the public interface of your class. It's a contract between the class designer/developer and the users of that class. When you define getters and setters, you should be committed to maintain them in future versions.

Attributes should only correspond the implementation of a given version of the class. In this way, the class developer may unilaterally change the implementation, hence the field, without breaking his/her commitment to maintain the interfaces.

Here is an example. Consider a class called Point. If you decide that a Point has x and y public attributes, then you may never change this. In contrast, if you have get/set X/Y methods, subsequent versions of the class may use various internal representations: rectangular coordinates (x, y), but also polar (r, theta), etc. All this without modifying the public interface.

A shorter version of your methods...

public void beDamaged(double damage) {
    health = Math.max(0, health-damage);
}

public void gainHealth(double gainedHp) {
    health = Math.min(maxHealth, health + gainedHp);
}

or even the following which can be called with +1 to gain, -1 to lose 1 hp.

public void adjustHealth(double adjustHp) {
    health = Math.max(0, Math.min(maxHealth, health + adjustHp));
}

If you're not maintaining any invariants, then public fields are the way to go. If you do need an invariant across multiple members, then you need private fields and encapsulation.

But if you can't come up with any better names than GetFoo and SetFoo for the methods, it's a good clue that your getters and setters are probably worthless.

.... pathetic content omitted....

EDIT

sorry for beeing a little too pathetic -must be the pills... The other answers are quite relevant and good

One advantage not yet mentioned for avoiding public fields: if there aren't any public fields, one may define an interface that includes all the public features of the class, have the class implement that interface, and then have everyplace that uses the class use the interface instead. If that is done, one may later design a class which has completely different methods and fields, but which implements the same interface, and use that class interchangeably with the original. If this is done, it may be useful to have the class implement a static factory method in addition to the constructor, and have the factory return an object of the interface type. Doing that would allow later versions of the factory to return an object of some other type. For example, one may come up with a low-cost version of the object in which many properties return constants; the factory could see if such an object would be suitable, and if so return one instead of the normal object.

Incidentally, the concept of using a mixture of constant and mutable objects in an adventure goes back at least to 1980. In Warren Robinett's "Adventure" cartridge for the 2600, each object has a number of pointers stored in ROM for things like position and state, so objects which aren't going to move (such as the castle gates or the "signature") don't need to have their position stored in RAM, and most grabbable objects (which don't have any state other than their position) won't need to store a state in RAM, but animated objects like the dragons and bat can store both state and position in RAM. On a machine with 128 bytes of RAM total, such savings were critical.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!