Oct 29, 2012

Evil : Getters and Setters where they're not required



We all programmers keep our instance variables private and automatically add public setters and getters to them. This is same as making the instance variables as public. Why we are exposing our variable in this way? 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.

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.

But, we should not add automatic getters and setters. Having getters and setters does not in itself break encapsulation. What does break encapsulation is having a getter and a setter for every data member (every field, in java lingo). That is one step away from making all data members public.

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

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.

Conclusion 


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.

2 comments:

Martin Andersson said...
This comment has been removed by the author.
Martin Andersson said...

Although I agree with you about the importance of "encapsulation" and other object oriented design principles, you are wrong about a few points.

You said: "Getters and Setters allow you to manipulate the internal state of objects from outside of the object."

Primitives and String objects are immutable, and so can other complex types be. So leaking them does not, cannot, change the internal state of the object. Also mind that one can always return an unmodifiable view of an internal state. Consider Arrays.asList() and Collections.unmodifiableXXX().

You said: "You can only specify methods, not fields, in an interface."

That is wrong too, at least when it comes to static fields.

And about your example, the Person class. I feel he should have a setName() and even a setSSN() method, if they are declared protected.

People usually go crazy about encapsulation, forgetting that if you do not explicitly ban inheritance by putting in the final keyword in the class declaration (preferred) or make his only constructor private, then anyone can subclass your type. And then what? A Homeless class that extends Person might not have a SSN! =)