202

Getters and setters are often criticized as being not proper OO. On the other hand, most OO code I've seen has extensive getters and setters.

When are getters and setters justified? Do you try to avoid using them? Are they overused in general?

If your favorite language has properties (mine does) then such things are also considered getters and setters for this question. They are the same thing from an OO methodology perspective. They just have nicer syntax.

To state the criticism simply: Getters and Setters allow you to manipulate the internal state of objects from outside of the object. This violates encapsulation. Only the object itself should care about its internal state.

And an example:
Procedural version of code:

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge) { fridge.cheese += 5; }

Mutator version of code:

class Fridge
{
     int cheese;
 void set_cheese(int _cheese) { cheese = _cheese; }
 int get_cheese() { return cheese; }

}

void go_shopping(Fridge fridge) { fridge.set_cheese(fridge.get_cheese() + 5);
}

The getters and setters made the code much more complicated without affording proper encapsulation. Because the internal state is accessible to other objects we don't gain a whole lot by adding these getters and setters.

Winston Ewert
  • 25,052

14 Answers14

183

Having getters and setters does not in itself break encapsulation. What does break encapsulation is automatically adding a getter and a setter for every data member (every field, in java lingo), without giving it any thought. While this is better than making all data members public, it is only a small step away.

The point of encapsulation is not that you should not be able to know or to change the object's state from outside the object, but that you should have a reasonable policy for doing it.

  • Some data members may be entirely internal to the object, and should have neither getters nor setters.

  • Some data members should be read-only, so they may need getters but not setters.

  • Some data members may need to be kept consistent with each other. In such a case you would not provide a setter for each one, but a single method for setting them at the same time, so that you can check the values for consistency.

  • Some data members may only need to be changed in a certain way, such as incremented or decremented by a fixed amount. In this case, you would provide an increment() and/or decrement() method, rather than a setter.

  • Yet others may actually need to be read-write, and would have both a getter and a setter.

Consider an example of a class Person. Let's say a person has a name, a social security number, and an age. Let's say that we do not allow people to ever change their names or social security numbers. However, the person's age should be incremented by 1 every year. In this case, you would provide a constructor that would initialize the name and the SSN to the given values, and which would initialize the age to 0. You would also provide a method incrementAge(), which would increase the age by 1. You would also provide getters for all three. No setters are required in this case.

In this design you allow the state of the object to be inspected from outside the class, and you allow it to be changed from outside the class. However, you do not allow the state to be changed arbitrarily. There is a policy, which effectively states that the name and the SSN cannot be changed at all, and that the age can be incremented by 1 year at a time.

Now let's say a person also has a salary. And people can change jobs at will, which means their salary will also change. To model this situation we have no other way but to provide a setSalary() method! Allowing the salary to be changed at will is a perfectly reasonable policy in this case.

By the way, in your example, I would give the class Fridge the putCheese() and takeCheese() methods, instead of get_cheese() and set_cheese(). Then you would still have encapsulation.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Dima
  • 11,852
46

The basic reason for getters and setters in Java is very simple:

  • You can only specify methods, not fields, in an interface.

Hence, if you want to allow a field to pass across the interface, you will need a reader and a writer method. These are traditionally called getX and setX for the field x.

25

From http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

JavaBean style:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-style:

connection.connect("dukie","duke");

Well, clearly I prefer the latter approach; it doesn't bleed implementation details, it's simpler and more concise, and all of the needed information is included with the method call, so it's easier to get it right. I also prefer setting private members using parameters in the constructor, whenever possible.

Your question is, when is a getter/setter justified? Perhaps when a mode change is needed, or you need to interrogate an object for some information.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

In thinking about it, when I first started coding in C#, I wrote a lot of code in the Javabeans style illustrated above. But as I gained experience in the language, I began doing more setting of members in the constructor, and using methods that looked more like the above OO style.

Robert Harvey
  • 200,592
21

When are getters and setters justified?

When the behavior "get" and "set" actually match up to behavior in your model, which is practically never.

Every other use is just a cheat because the behavior of the business domain is not clear.

Edit

That answer might have come across as flippant, so let me expand. The answers above are mostly correct but focus on the programming paradigms of OO design and some what miss the big picture. In my experience this leads people to think that avoiding gettings and setters is some academic rule for OO programming languages (e.g people think replacing getters with properties is grand)

In fact it is a consequence of the design process. You don't have to get into interfaces and encapsulation and patterns, arguing over whether it does or doesn't break these paradigms and what is or isn't good OO programming. The only point that ultimately matters is that if there is nothing in your domain that works like this by putting them in you are not modeling your domain.

The reality is that it is highly unlikely that any system in your domain space has getters and setters. You cannot walk up to the man or woman who is responsible for payroll and simply say "Set this salary to X" or "Get me this salary". Such behavior simply doesn't exist

If you are putting this into your code you are not designing your system to match the model of your domain. Yes that breaks interfaces and encapsulation, but that isn't the point. The point is that you are modelling something that doesn't exist.

More over you are probably missing an important step or process, because there is probably a reason I can't just walk up to pay roll and say set this salary to X.

When people use getters and setters they tend to push the rules for this process into the wrong place. This is moving away from your domain even more. Using the real world example, it is like salary assuming that the random person who walked in has the permission to get these values otherwise he wouldn't be asking for them. Not only is that not how the domain is, it is in fact lying about how the domain is.

Cormac Mulhall
  • 5,176
  • 2
  • 21
  • 19
16

As a general rule, getters and setters are a bad idea. If a field isn't logically part of the interface and you make it private, that's fine. If it is logically part of the interface and you make it public, that's fine. But if you make it private and then turn around and make it effectively public again by providing a getter and setter, you're back to where you started except your code is now more verbose and obfuscated.

Obviously, there are exceptions. In Java, you might need to use interfaces. The Java standard library has backward compatibility requirements so extreme as to outweigh normal measures of code quality. It's even possible that you may actually be dealing with the legendary but rare case where there's a good chance you may later replace a stored field with on the fly calculation without otherwise breaking the interface. But these are exceptions. Getters and setters are an anti-pattern that needs special justification.

rwallace
  • 1,208
4

whether field is accesible directly or via method is not realy important.

Class invariants (usefull ones) is important. And to preserve them, we sometimes need to not be able to change something from outside. Eg. if we have class Square with separete width and height, changing one of them makes it become something else than square. So we need method changeSide If it was Rectangle, we could have setters/public field. But setter than would test whether its greater than zero would be better.

And in concrete languages (eg. java) are reasons why we need those methods (interfaces). And another reason for method is compatibility (source and binary). So its easier to add them then think whether public field would suffice.

btw. I like to use simple immutable value holding classes with public final fields.

user470365
  • 1,259
3

You may want to change your internals to whatever while keeping the interfaces the same. If your interfaces don't vary, them you code won't break. You still can change your internals as you wish.

1

Firstly there are broadly two types of objects which i will comment on, value and service types.

Service types should never have setters, whatever dependencies they require should not be gettable. THe best way to pass dependencies is via a constructor or factory that way all instances are fully formed from the beginning, plain and simple.

Value types should also be immutable, on the oher hand there are times when this is not practical such as an ORM, or some other mapping such as from a widget to an object. All other value types that get passed around the system from one layer or part to another should be immutable and should not have setters.

mP01
  • 291
1

If using getters and setters feels complicated, the problem might be the language, not the concept itself.

Here's the code from the second example written in Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Notice it looks a lot like the first example in Java? When getters and setters are treated as first class citizens, they're not a chore to use, and the added flexibility can sometimes be a real boon - for example, we could decide to return a default value for cheese on a new fridge:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Of course there will be many variables that shouldn't be exposed publicly at all. Needlessly exposing internal variables will make your code worse, but you can hardly blame that on getters and setters.

0

Consider a Size class which encapsulates width and height. I can eliminate setters by using the constructor but how does that help me draw a rectangle with Size? Width and height are not internal data to the class; they're shared data that must be available to the consumers of Size.

Objects consist of behaviors and state - or attributes. If there is no exposed state then only behaviors are public.

Without state, how would you sort a collection of objects? How would you search for a specific instance of an object? If you only use constructors, what happens when your object has a long list of attributes?

No method parameter should ever be consumed without validation. Therefore it is laziness to write:

setMyField(int myField){
    this.myField = myField;
}

If you do write it that way then you have at least prepared for using the setter for validation; it's better than a public field - but just barely. But at least you have a consistent public interface that you can come back and put in validation rules without breaking the code of your customers.

Getters, setters, properties, mutators, call them what you will, but they're necessary.

Jalayn
  • 9,827
Dale
  • 1
0

My approach is this -

When I expect to diddle with the data later on, a getter/setter is justified. Also, if change is happening, I often push the data into a getter/setter.

If it's a POD structure, I leave the slots available.

On a more abstract level, the question is "who manages the data", and that depends on the project.

Paul Nathan
  • 8,560
  • 1
  • 34
  • 41
0

In Swift, there are variables (class, instance, and static variables) which are quite the same as properties. And for every variable, you can add a getter and a setter if you wish to do so, plus code to be called before the setter is called (with a parameter "newValue") and code to be called after the setter is called (with a parameter "newValue).

The variable itself can be private or public (or fileprivate, or internal, or open; these are Swift specific), and the setter can be private.

If it's private it doesn't expose anything (and fileprivate only exposes within one source file). If you have a private setter, it can't be modified without control so you are always safe. And if you have a getter, then it doesn't expose any internal state. It only exposes what the interface says it exposes.

Perfectly object oriented. What you see may be the internal state, maybe it's not. I might have four temperate classes Kelvin, Celsius, Fahrenheit and Reamur which all store temperature in degree Kelvin internally - but only one exposes this directly as a property. And you don't know which one (since the could all four store the temperature in Celsius), and it doesn't matter. Only the interface matters.

The only difference between a variable and a function is mostly that functions have () appended :-) And you don't have a "get" or "set" prefix. And variables should be used for things that are reasonably fast.

BTW. Swift allows fridge.cheese += 5 (no semicolon), calling a "getter" and a "setter".

gnasher729
  • 49,096
0

Having a public member variable, then changing it to private with a public getter and setter is pointless, but somehow that became quite fashionable in C++. The one time where it is justified is if you have 20 properties implemented through meaningful setters and getters, and five through member variables, and you want consistent access.

Other languages have “properties”. Properties are used as if they were members. Behind the scenes there will be getters and setters which may be very simple or very complex. For example a getter which uses a cached value read from a database if needed, with a setter writing to the database. Or setters that check if the object is displayed on the screen and update the screen. Or a getter on a “rectangle” object for the area, which multiplies width and height, which are calculated from top and bottom, left and right border.

The fact that getter and setter are called is hidden in these languages.

gnasher729
  • 49,096
-1

If getters and setters violate encapsulation and true OO, then I'm seriously in trouble.

I always felt an object represents whatever best comes to mind that you need it to do.

I have just finished writing a program that generates Mazes in Java, and I have class that represents "Maze Squares". I have data in this class that represents coordinates, walls and booleans etc.

I have to have some way to change/manipulate/access this data! What do I do without getters and setters? Java doesn't use properties and setting all my data that is local to this class to public is DEFINITELY a violation of encapsulation and OO.

SusanW
  • 1,034
Nayrb
  • 2,514
  • 1
  • 19
  • 22