Are getters and setters poor design? Contradictory advice seen

81,603

Solution 1

There is also the point of view that most of the time, using setters still breaks encapsulation by allowing you to set values that are meaningless. As a very obvious example, if you have a score counter on the game that only ever goes up, instead of

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

it should be

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

This is perhaps a bit of a facile example. What I'm trying to say is that discussing getter/setters vs public fields often obscures bigger problems with objects manipulating each others' internal state in an intimate manner and hence being too closely coupled.

The idea is to make methods that directly do things you want to do. An example would be how to set enemies' "alive" status. You might be tempted to have a setAlive(boolean alive) method. Instead you should have:

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

The reason for this is that if you change the implementation that things no longer have an "alive" boolean but rather a "hit points" value, you can change that around without breaking the contract of the two methods you wrote earlier:

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }

Solution 2

  • Very evil: public fields.
  • Somewhat evil: Getters and setters where they're not required.
  • Good: Getters and setters only where they're really required - make the type expose "larger" behaviour which happens to use its state, rather than just treating the type as a repository of state to be manipulated by other types.

It really depends on the situation though - sometimes you really do just want a dumb data object.

Solution 3

You've already had a lot of good answers on this, so I'll just give my two cents. Getters and setters are very, very evil. They essentially let you pretend to hide your object's internals when most of the time all you've done is tossed in redundant code that does nothing to hide internal state. For a simple POJO, there's no reason why getName() and setName() can't be replaced with obj.name = "Tom".

If the method call merely replaces assignment, then all you've gained by preferring the method call is code bloat. Unfortunately, the language has enshrined the use of getters and setters in the JavaBeans specification, so Java programmers are forced to use them, even when doing so makes no sense whatsoever.

Fortunately, Eclipse (and probably other IDEs as well) lets you automatically generate them. And for a fun project, I once built a code-generator for them in XSLT. But if there's one thing I'd get rid of in Java, its the over-dependence on getters and setters.

Solution 4

Getters and setters enforce the concept of encapsulation in object-oriented programming.

By having the states of the object hidden from the outside world, the object is truly in charge of itself, and cannot be altered in ways that aren't intended. The only ways the object can be manipulated are through exposed public methods, such as getters and setters.

There are a few advantages for having getters and setters:

1. Allowing future changes without modification to code that uses the modified class.

One of the big advantage of using a getter and setter is that once the public methods are defined and there comes a time when the underlying implementation needs to be changed (e.g. finding a bug that needs to be fixed, using a different algorithm for improving performance, etc.), by having the getters and setters be the only way to manipulate the object, it will allow existing code to not break, and work as expected even after the change.

For example, let's say there's a setValue method which sets the value private variable in an object:

public void setValue(int value)
{
    this.value = value;
}

But then, there was a new requirement which needed to keep track of the number of times value was changed. With the setter in place, the change is fairly trivial:

public void setValue(int value)
{
    this.value = value;
    count++;
}

If the value field were public, there is no easy way to come back later and add a counter that keeps track of the number of times the value was changed. Therefore, having getters and setters are one way to "future-proof" the class for changes which may come later.

2. Enforcing the means by which the object can be manipulated.

Another way getters and setters come in handy is to enforce the ways the object can be manipulated, therefore, the object is in control of its own state. With public variables of an object exposed, it can easily be corrupted.

For example, an ImmutableArray object contains an int array called myArray. If the array were a public field, it just won't be immutable:

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a's contents have been changed.

To implement a truly immutable array, a getter for the array (getArray method) should be written so it returns a copy of its array:

public int[] getArray()
{
    return myArray.clone();
}

And even if the following occurs:

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

The ImmutableArray is indeed immutable. Exposing the variables of an object will allow it to be manipulated in ways which aren't intended, but only exposing certain ways (getters and setters), the object can be manipulated in intended ways.

I suppose having getters and setters would be more important for classes which are part of an API that is going to be used by others, as it allows keeping the API intact and unchanged while allowing changes in the underlying implementation.

With all the advantages of getters and setters said, if the getter is merely returning the value of the private variable and the setter is merely accepting a value and assigning it to a private variable, it seems the getters and setter are just extraneous and really a waste. If the class is going to be just for internal use by an application that is not going to be used by others, using getters and setters extensively may not be as important as when writing a public API.

Solution 5

They absolutely are evil.

@coobird unfortunately they absolutely do not "enforce the concept of encapsulation", all they do is make you think you're encapsulating data when in fact you're exposing data via a property with delusions of method grandeur. Anything a getter/setter does a public field does better.

First, if you want public data, make it public, get rid of the getter & setter methods to reduce the number of methods the client has to wade through and make it cognitively simpler for the client to change it's value by eg.

object.field = value;

instead of the more cognitively intense

object.setField(value);

where the client must now check the getter/setter method to see if it has any side-effects.

Second, if you really need to do something else in the method, why call it a get/set method when it's got more responsibilities than simply getting or setting? Either follow the SRP or call the method something that actually tells you what the whole method does like Zarkonnen's examples he mentioned eg.

public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

instead of

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}

where does the setAlive(boolean) method tell the client that as a side-effect it'll remove the object from the world? Why should the client have any knowledge about the isAlive field? Plus what happens when the object is re-added to the world, should it be re-initialised? why would the client care about any of that?

IMHO the moral is to name methods to say exactly what they do, follow the SRP and get rid of getters/setters. If there's problems without getters/setters, tell objects to do their own dirty work inside their own class instead of trying to do things with them in other classes.

here endeth my rant, sorry about that ;)

Share:
81,603

Related videos on Youtube

Mike B
Author by

Mike B

Experienced Software Engineer with a decade of experience building software.

Updated on October 10, 2020

Comments

  • Mike B
    Mike B over 3 years

    I'm currently working on a simple game in Java with several different modes. I've extended a main Game class to put the main logic within the other classes. Despite this, the main game class is still pretty hefty.

    After taking a quick look at my code the majority of it was Getters and Setters (60%) compared to the rest that is truly needed for the logic of the game.

    A couple of Google searches have claimed that Getters and Setters are evil, whilst others have claimed that they are necessary for good OO practice and great programs.

    So what should I do? Which should it be? Should I be changing my Getters and Setters for my private variables, or should I stick with them?

  • Benoît
    Benoît over 15 years
    Since it's not obvious that your "somewhat evil" cases won't eventually need a "larger behaviour", i'd go for the "good" way, just in case, so you don't have to change your code everywhere if you change your mind !
  • Mike B
    Mike B over 15 years
    Each of these values change as the game goes on, so no constants are required. However, these variables are only really used for holding settings for the actual game (i.e. mode, timeLimit). This is why I cannot help but feel that they are somewhat unnecessary.
  • user5880801
    user5880801 over 15 years
    Let me go a little bit futher. Should the internals of the class use getters/setters instead of directly accessing the fields? My opinion is that yes.
  • Jon Skeet
    Jon Skeet over 15 years
    @EnderMB: If the variables are meant to be settings, then encapsulate them in a settings class.
  • Mike B
    Mike B over 15 years
    The problem is that whilst there are several settings for this game they are minute in their differences, with only a couple of extra methods typically needing to be applied in the sister classes to provide different values.
  • Mike B
    Mike B over 15 years
    Thanks for the reply. In response to your edit, if I call two setters from within every sister class it would be good OO practice to remove both setter methods in the main Game class to turn then into one group setter?
  • Mike B
    Mike B over 15 years
    So, would it be a good idea to create a separate Settings class, extend the Settings to the game, then extend the Game to its sister classes? If I do that am I still able to edit settings from within the sister classes?
  • Michael Borgwardt
    Michael Borgwardt over 15 years
    Exactly - if those values are always set together, it's better to convery this information by having only one setter. It would be even better to put them together in a class, if there are more than two, or if there are methods that operate only on them and which could also be put into that class.
  • tehvan
    tehvan over 15 years
    No, you don't want Game to extend Settings. Settings serve as a value object... a plain pojo with getters and setters.
  • Lemon
    Lemon over 15 years
    And as a bi-effect, you can then serialize and dump the settings class to a file, and you have saved your settings :)
  • Brett
    Brett over 15 years
    It generally doesn't really make any sense to change their implementation.
  • Mike B
    Mike B over 15 years
    Let me see if I get this: Create a Settings class, put all my settings variables, as well as their getters/setters within this class, then call something like Settings s = new Settings() and then use s.getTimeLimit(). I'm struggling to understand from what I've read about value objects on Google.
  • Bill the Lizard
    Bill the Lizard over 15 years
    You left out the reason why they're evil.
  • Bill the Lizard
    Bill the Lizard over 15 years
    What if your object changes so that one field needs a getter and a setter? If that field is being used in 100 different places, that's 100 different changes you need to make. If you use getters and setters from the start you only need to change it in one place, the object that's changing.
  • James Socol
    James Socol over 15 years
    That sounds right to me.
  • user3443701
    user3443701 over 15 years
    If you need to expose that variable to 100 different places, use a getter and setter. But is it really good OO practice to have 100 different places playing with your object's internal data?
  • Bill the Lizard
    Bill the Lizard over 15 years
    @Terry: That object could be used in 100 different projects, not just called in 100 different places in one project.
  • Bill the Lizard
    Bill the Lizard over 15 years
    ...also, I should be saying "class" instead of "object".
  • rtperson
    rtperson over 15 years
    I fixed it so my objection is clearer.
  • Daniel Earwicker
    Daniel Earwicker about 15 years
    @Mo - There's a lot to be said for a plain old pojo object :)
  • Chris Noe
    Chris Noe almost 14 years
    Just don't forget to do a YAGNI check when tempted to promote a POJO to a Bean. Accessors, serialization, etc. are a form of complexity: stackoverflow.com/questions/601721/decoupling-vs-yagni
  • Jesse Barnum
    Jesse Barnum over 13 years
    ob.name = "Tom" may work the same as ob.setName("Tom"), but it's inferior because it doesn't allow you to add behavior later without changing the API. This is Object Oriented Programming 101.
  • rtperson
    rtperson over 13 years
    @Jesse Barnum - I was aware that, in writing this, I was voicing a heterodox opinion. As we move into an increasingly multicore world, however, there is a growing number of us who find the Object Oriented model increasingly coming up short. It is, for one, an absolute mess to try to share state across multiple threads. For these and other reasons, I've recently become enamored of languages like Haskell, where immutability and the functional paradigm make it easier to get more done in less space. I'm not anti-Java, just frustrated by its limitations and its bureaucratic verboseness. YMMV.
  • dseibert
    dseibert over 13 years
    It's important to note here that validation can be done within these "action" methods.
  • Sidi
    Sidi almost 13 years
    No you can just keep the setter **private ** anyway. Immutability is an other topic, by definition imuttable is when you can't change it's value after created like String for example. So this in your example it's a bug Look at C# or language supoorting properties.
  • wulfgarpro
    wulfgarpro almost 13 years
    @Simo - the backing array is immutable in the example.
  • wulfgarpro
    wulfgarpro almost 13 years
    @coobird - even if the getters/setters are simply returning/setting with no additional logic, do they not future proof the API when additional logic might be needed?
  • coobird
    coobird almost 13 years
    @wulfgar.pro: Yip, they sure do, and that's one of the advantages of using setters and getters.
  • nathan
    nathan over 12 years
    As an aside, think of a BankAccount object, would you prefer a setBalance() method or an increaseBalance() method?
  • nathan
    nathan over 12 years
    Wanted to comment on Bill the Lizard's post...
  • smentek
    smentek about 12 years
    Getters and setters do not enforce anything. javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html. Good encapsulation == your API goes after some real business logic not after row data. For instance object Car may have method: accellerate(Integer spead) { sets speed and do 10 other things - good encapsulation} or setSpeed(Integer speed) { //just setter - poor encapsulation}.
  • artbristol
    artbristol over 11 years
    In all my years of programming, I've never needed to change a getter or setter to do something differently. For a published API, yes to getters and setters. Within a project, where you can see all the field's uses in your IDE, a public field is just as good and less verbose, and you can easily use encapsulate field if you need specific behaviour.
  • zanetu
    zanetu about 11 years
    I don't think the "alive" example is convincing. Instead of using "public void damage(int damage) { hp -= damage; }", one may use "public void damage(int damage) { hp -= damage; if (hp <= 0) setAlive(false); }" along with "public void setAlive(boolean alive){if (!alive) hp = 0;}", which still follows getter/setter pattern and makes more sense to me.
  • Onur A.
    Onur A. almost 11 years
    "using setters still breaks encapsulation by allowing you to set values that are meaningless." nonsense, you can validate the value to be set in your setter method thus can avoid meaningles values
  • DanMan
    DanMan over 9 years
    This, 1k times over. Don't take the state data from the object, change it somewhere else, and put it back in. Give it all the data it needs to do the work itself. That's like OOP 101.
  • David Lavender
    David Lavender about 9 years
    This is great from a pure OO point of view. However, in the real-world, most projects would achieve the above with a Game POJO containing getters and setters. That gets sent to a GameService.updateGame method (probably containing a GameValidator to ensure that Game scores can only go up). This approach requires having getters and setters so that your POJO can be used as a transport object between application layers. I'm not saying I like it... but it's out there and seems relevant to the question...
  • prograhammer
    prograhammer almost 9 years
    @artbristol is right, I think it is a little bit of a misconception that we think the best place to add functionality (like "logging" or "validation" or "mutating") is at the setter. I think ideally we would do these sort of things somewhere else. For example, we would be more inclined to add logging to somewhere with meaning (ie. saving or emailing) instead of at a general setter. Or we may need to do validation based on context (ie. are we validating for saving? or validation for emailing?). Plus, there are many times where you want to validate everything together instead of individually.
  • prograhammer
    prograhammer almost 9 years
    To further add to this, it is a misconception to think the best place to add functionality (like "logging" or "validation" or "mutating") is at the setter. Ideally we would do these sort of things somewhere else. For example, we would be more inclined to add logging to somewhere with meaning (ie. saving or emailing) instead of at a general setter. Or we may need to do validation based on context (ie. are we validating for saving? or validation for emailing?). Plus, there are many times where you want to validate everything together instead of individually.
  • LegendLength
    LegendLength over 8 years
    @artbristol And C# basically follows that philosophy by allowing you to create a public field and later on change it to a property (although there are some small issues with that unfortunately).
  • Haakon Løtveit
    Haakon Løtveit over 8 years
    You can have the same thing in Java if you use Lombok. IDE support for lombok exists for IntelliJ, Eclipse, and Emacs.
  • AgilePro
    AgilePro over 8 years
    Excellent answer, and very well put. More support for this point of view at: Brainless Getters & Setters are a Waste
  • magallanes
    magallanes over 8 years
    Adding logic to the encapsulation is evil. Sometimes is a needing evil but its still evil. If i have a function named "setValue", then its about setting a value and not other task.
  • magallanes
    magallanes over 8 years
    IMHO, most programmers use setter and getter because its auto generated (and is used for justify working hours on it). Is the same with the (used everywhere in some projects) Interfaces. Interface has a purpose, if the purpose is nil then don't use it.
  • CoronA
    CoronA about 8 years
    I consider overriding a setter is even more evil. Users of the API consider a setter to change just one variable - and not to do complex things. The "correct" way would be to rename the setter in a way that later changes do not destroy user expectations.
  • Janez Kuhar
    Janez Kuhar over 7 years
    @JonSkeet "make the type expose "larger" behaviour which happens to use its state, rather than just treating the type as a repository of state to be manipulated by other types." I have no idea what you mean by this. Please explain.
  • Jon Skeet
    Jon Skeet over 7 years
    @JanezKuhar: I mean that rather than making the caller perform multiple operations which manipulate the state in several steps but basically leave the type as a bag of state with no meaning, expose methods which perform meaningful operations which might manipulate (or at least use) those multiple pieces of state.
  • Will
    Will almost 7 years
    As DanMan says, OOP 101 says, "Tell, don't ask." Tell the object to do a task for you and trust it to do it. Don't micro-manage it with sets() and gets() and do the job yourself, that's almost like having a dog and barking yourself.