45

I got into an interesting internet argument about getter and setter methods and encapsulation. Someone said that all they should do is an assignment (setters) or a variable access (getters) to keep them "pure" and ensure encapsulation.

  • Am I right that this would completely defeat the purpose of having getters and setters in the first place and validation and other logic (without strange side-effects of course) should be allowed?
  • When should validation happen?
    • When setting the value, inside the setter (to protect the object from ever entering an invalid state - my opinion)
    • Before setting the value, outside the setter
    • Inside the object, before each time the value is used
  • Is a setter allowed to change the value (maybe convert a valid value to some canonical internal representation)?
Botond Balázs
  • 1,463
  • 2
  • 12
  • 13
  • 20
    The best thing about getters and setters is the ability *not to have them*, i.e. leave of the setter and you have a read-only property, leave of the getter and you have config option whose current value is nobody's business. – Michael Borgwardt Nov 25 '12 at 21:23
  • 8
    @MichaelBorgwardt Leave both out to have a clean “tell, don’t ask” interface. Properties = potential code smell. – Konrad Rudolph Nov 26 '12 at 09:06
  • Obligatory link: http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html – user281377 Nov 26 '12 at 09:24
  • 2
    Setters can also be used for raising an *Event*. – John Isaiah Carmona Nov 26 '12 at 09:42
  • @KonradRudolph I agree with your statement, though I'd like to really emphasize the word "potential." – Phil Nov 26 '12 at 21:36
  • In Java, using getter and setter methods enables you to refactor accessors into an interface. The same applies for C# properties, C++ pure virtual getter/setter methods, Common Lisp with accessor methods or same named method and setf method (instead of using `with-slots`), etc. By inference, this is a flexibility point in most languages. They probably should start out as simple accessors, but using them allows you to decouple from implementation details, such as if they start delegating into an inner object in a future version. – acelent Nov 27 '12 at 19:24

8 Answers8

38

I remember having a similar argument with my lecturer when learning C++ at university. I just couldn't see the point of using getters and setters when I could make a variable public. I understand better now with years of experience and I've learned a better reason than simply saying "to maintain encapsulation".

By defining the getters and setters, you will provide a consistent interface so that if you should wish to change your implementation, you're less likely to break dependent code. This is especially important when you classes are exposed via an API and used in other apps or by 3rd parties. So what about the stuff that goes into the getter or setter?

Getters are generally better off implemented as a simple dumbed-down passthrough for access to a value because this makes their behaviour predictable. I say generally, because I've seen cases where getters have been used to access values manipulated by calculation or even by conditional code. Generally not so good if you are creating visual components for use at design time, but seemingly handy at run time. There's no real difference however between this and using a simple method, except that when you use a method, you are generally more likely to name a method more appropriately so that the functionality of the "getter" is more apparent when reading the code.

Compare the following:

int aValue = MyClass.Value;

and

int aValue = MyClass.CalculateValue();

The second option makes it clear that the value is being calculated, whereas the first example tells you that you are simply returning a value without knowing anything about the value itself.

You could perhaps argue that the following would be clearer:

int aValue = MyClass.CalculatedValue;

The problem however is that you are assuming that the value has already been manipulated elsewhere. So in the case of a getter, while you may wish to assume that something else might be going on when you are returning a value, it is difficult to make such things clear in the context of a property, and property names should never contain verbs otherwise it makes it difficult to understand at a glance whether the name used should be decorated with parentheses when accessed.

Setters are a slightly different case however. It is entirely appropriate that a setter provide some additional processing in order to validate the data being submitted to a property, throwing an exception if setting a value would violate the defined boundaries of the property. The problem that some developers have with adding processing to setters however is that there is always a temptation to have the setter do a little more, such as perform a calculation or a manipulation of the data in some manner. This is where you can get side-effects that can in some cases be either unpredictable or undesirable.

In the case of setters I'm always apply a simple rule of thumb, which is to do as little as possible to the data. For example, I will usually allow either boundary testing, and rounding so that I can both raise exceptions if appropriate, or avoid unnecessary exceptions where they can be sensibly avoided. Floating point properties are a good example where you might wish to round excessive decimal places to avoid raising an exception, while still allowing in range values to be entered with a few additional decimal places.

If you apply some sort of manipulation of the setter input, you have the same problem as with the getter, that it is difficult to allow others to know what the setter is doing by simply naming it. For example:

MyClass.Value = 12345;

Does this tell you anything about what is going to happen to the value when it is given to the setter?

How about:

MyClass.RoundValueToNearestThousand(12345);

The second example tells you exactly what is going to happen to your data, while the first won't let you know if you value is going to be arbitrarily modified. When reading code, the second example will be much clearer in it's purpose and function.

Am I right that this would completely defeat the purpose of having getters and setters in the first place and validation and other logic (without strange side-effects of course) should be allowed?

Having getters and setters isn't about encapsulation for the sake of "purity", but about encapsulating in order to allow code to be easily refactored without risking a change to the class's interface which would otherwise break the class's compatibility with calling code. Validation is entirely appropriate in a setter, however there is a small risk is that a change to the validation could break compatibility with calling code if the calling code relies on the validation occurring in a particular way. This is a generally rare and relatively low-risk situation, but it should be noted for the sake of completeness.

When should validation happen?

Validation should happen within the context of the setter prior to actually setting the value. This ensures that if an exception is thrown, the state of your object won't change and potentially invalidate its data. I generally find it better to delegate validation to a separate method which would be the first thing called within the setter, in order to keep the setter code relatively uncluttered.

Is a setter allowed to change the value (maybe convert a valid value to some canonical internal representation)?

In very rare cases, maybe. In general, it is probably better not to. This is the sort of thing best left to another method.

S.Robins
  • 11,385
  • 2
  • 36
  • 52
  • re: change the value, it might be reasonable to set it to -1 or some NULL flag token if the setter is passed an illegal value – Martin Beckett Nov 25 '12 at 21:30
  • 1
    There are a couple of problems with this. Arbitrarily setting a value creates a deliberate and unclear side-effect. Also, it doesn't allow the calling code to receive feedback which could be used to better deal with illegal data. This is particularly important with values at the UI level. To be fair though, one exception I just thought of could be if allowing multiple date formats as an input, while storing the date in a standard date/time format. One could at a stretch argue that this particular "side effect" is a normalization of the data at validation, provided the input data is legal. – S.Robins Nov 25 '12 at 21:51
  • Yes, one of the justifications of a setter is that it should return true/false if the value could be set. – Martin Beckett Nov 25 '12 at 21:53
  • 1
    "Floating point properties are a good example where you might wish to round excessive decimal places to avoid raising an exception" In what circumstance does having too many decimal places raise an exception? – Mark Byers Nov 26 '12 at 09:30
  • Surely the difference between `MyClass.Value` and `MyClass.CalculateValue()` is purely implementational. If your classes are well encapsulated and the return value is of a predictable type then why should it be important to the code making the call whether the value is calculated or not? – glenatron Nov 26 '12 at 13:18
  • 2
    I see changing the value to a canonical representation as a form of validation. Defaulting missing values (e.g., the current date if a timestamp contains only the time) or scaling a value (.93275 -> 93.28%) to ensure internal consistency should be OK, but any such manipulations should be explicitly called out in the API documentation, especially if they are an uncommon idiom within the API. – TMN Nov 26 '12 at 16:53
  • @MarkByers You may wish to validate input of floating point values by applying a rule that only a fixed number of decimal places is allowed. In that circumstance, you could either raise an exception if too many decimal places are input, or you could simply round the input value. This may be preferable to raising an exception. I agree though that the sentence you've quoted probably needs to be modified. I'll think about this and edit. :) – S.Robins Nov 26 '12 at 20:31
  • @glenatron You miss the point that I'm writing about. Side effects within properties aren't always predictable. When you set a property Value, you expect that same value to be returned. When you call a function that *may* use a value, then you assume the output won't necessarily be that value itself. – S.Robins Nov 26 '12 at 20:39
  • @S.Robins At first that seemed completely pointless to me, but I suppose if it is actually pointless to have a class member that is always the same when it is set and retrieved then it is pointless to have a setter/getter configured for that member. Presumably working the way you describe, you could use any properties that can be set by a calling class to recall the initial state of the object. Any values that are the outcome of operations performed by the class are then made available through their own properties or through method calls? – glenatron Nov 27 '12 at 10:37
  • I've created a [Chat Session](http://chat.stackexchange.com/rooms/6530/programmers-watercooler) to allow these comments to continue as needed. – S.Robins Nov 27 '12 at 10:52
21

If the getter/setter simply mirrors the value then there is no point in having them, or in making the value private. There is nothing wrong with making some member variables public if you have a good reason. If you are writing a 3d point class then having .x, .y ,.z public makes perfect sense.

As Ralph Waldo Emerson said, "A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and the designers of Java."

Getters/setters are useful where there may be side effects, where you need to update other internal variables, recalculate cached values and protect the class from invalid inputs.

The usual justification for them, that they hide the internal structure, is generally the least useful. eg. I have these points stored as 3 floats, I might decide to store them as strings in a remote database so I'll make getters/setters to hide them, as if you could do that without having any other effect on the caller's code.

Martin Beckett
  • 15,776
  • 3
  • 42
  • 69
  • 1
    Wow, the designers of Java are divine? –  Nov 25 '12 at 20:01
  • @delnan - obviously not - or they would rhyme better ;-) – Martin Beckett Nov 25 '12 at 20:03
  • Would it be valid to create a 3d point in which x,y,z are all Float.NAN? – Andrew T Finnell Nov 25 '12 at 23:58
  • 4
    I disagree with your point that "the usual justification" (hiding internal structure) is the least useful property of getters and setters. In C#, it's definitely useful to make the property an interface whose underlying structure can be changed - for example, if you only want a property available for enumeration, it's much better to say `IEnumerable` than forcing it to something like `List`. Your example of database access I would say is violating single responsibility - mixing the representation of the model with how it is persisted. – Brandon Linton Nov 26 '12 at 00:04
  • @AndrewFinnell - it might be a good way of flagging points as impossible/not valid/deleted etc – Martin Beckett Nov 26 '12 at 00:33
  • @MartinBeckett I guess that is the problem. If it is NOT valid, then there is no way to prevent that value from being set. But using getter and setters you can prevent it. – Andrew T Finnell Nov 26 '12 at 16:44
  • @AndrewFinnell - no I meant it may be very useful to deliberately set a variable to NaN. If your algorithm needs to flag certain points to ignore, then NaN is probably safer than 0 or -1, or 999999 for a real world use of floating point. – Martin Beckett Nov 26 '12 at 16:50
  • Yes I understand. I am taking the opposite approach. The problem with making member variables public is that you cannot enforce constraints. – Andrew T Finnell Nov 26 '12 at 18:17
  • What if you want to subclass point to a "PositivePoint" which in the setters will verify that the value passed is indeed positive? Also is it correct that Ralph Waldo Emerson lived long before Java and which makes your "quote" like statement simply your own opinion? – findusl Aug 07 '19 at 16:05
9

Meyer's Uniform Access Principle: "All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation." is the main reason behind the getters/setters, aka Properties.

If you decide to cache or lazily compute one field of a class then you can change this anytime if you have only exposed property accessors and not the concrete data.

Value objects, simple structures don't need this abstraction but a fully fledged class does, in my opinion.

Karl
  • 517
  • 2
  • 4
2

A common strategy for designing classes, which was introduced through the Eiffel language, is Command-Query Separation. The idea is that a method should either tell you something about the object or tell the object to do something, but not to do both.

This only relates to the public interface of the class, not the internal representation. Consider a data model object that's backed by a row in the database. You could create the object without loading the data in, then the first time you call a getter it actually does the SELECT. That's fine, you might be changing some internal details about how the object's represented but you're not changing how it appears to clients of that object. You should be able to call getters multiple times and still get the same results, even if they do different work to return those results.

Similarly a setter looks, contractually, like it just changes the state of an object. It might do that in some convoluted fashion—writing an UPDATE to a database, or passing the parameter on to some internal object. That's fine, but doing something unrelated to setting the state would be surprising.

Meyer (the creator of Eiffel) also had things to say about validation. Essentially, whenever an object is quiescent it should be in a valid state. So just after the constructor has finished, before and after (but not necessarily during) every external method call, the object's state should be consistent.

It's interesting to note that in that language, the syntax for calling a procedure and for reading an exposed attribute both look the same. In other words, a caller can't tell whether they're using some method or working directly with an instance variable. It's only in languages that don't hide this implementation detail where the question even arises—if callers couldn't tell, you could switch between a public ivar and accessors without leaking that change into client code.

1

Another acceptable thing to do is cloning. In some cases, you need to make sure, that after someone gives your class eg. a list of something, he cannot change it inside your class (nor change the object in that list). Therefore you make deep copy of parameter in setter and return deep copy in getter. (Using immutable types as parameters is annother option, but above is assuming it is not possible) But don't clone in accessors if it is not needed. Its easy to think (but not appropriately) about properies/getters and setters as constant cost operations, so this is performance landmine waiting for user of api.

user470365
  • 1,229
  • 6
  • 8
1

There's not always a 1-1 mapping between property accessors and the ivars that store the data.

For example, a view class might provide a center property even though there's no ivar that stores the center of the view; setting center causes changes to other ivars, like origin or transform or whatever, but clients of the class don't know or care how center is stored provided that it works correctly. What should not happen, though, is that setting center causes things to happen beyond whatever is necessary to save the new value, however that's done.

Caleb
  • 38,959
  • 8
  • 94
  • 152
0

The best part about setters and getters is that they make it easy to alter the rules of an API without changing the API. If you detect a bug, it's much more likely that you can fix the bug in the library and not have every consumer update its code base.

AlexanderBrevig
  • 525
  • 2
  • 6
-4

I tend to believe that setters and getters are evil and should only be used in classes that are managed by a framework/container. A proper class design should not yield getters and setters.

Edit: a well-written article on this subject.

Edit2: public fields are a nonsense in an OOP approach; by saying that getters and setters are evil I do not mean that they should be replaced by public fields.

Random42
  • 10,370
  • 10
  • 48
  • 65
  • 1
    I think you're missing the point of the article, which suggests using getters/setters sparingly and to avoid exposing class data unless necessary. This IMHO is a sensible design principle which should be applied deliberately by the developer. In terms of creating properties on a class, you *could* simply expose a variable, but this makes it harder to provide validation when the variable is set, or to draw the value from an alternate source when "got", essentially violating encapsulation, and potentially locking you into a difficult to maintain interface design. – S.Robins Nov 27 '12 at 10:36
  • @S.Robins In my opinion public getters and setters are wrong; public fields are more wrong (if that is comparable). – Random42 Nov 27 '12 at 10:40
  • @methodman Yes, I agree that a public field is wrong, however a public property can be useful. That property can be used to provide a place for validation or events related to the setting or returning of the data, depending on the specific requirement at the time. Getters and setters themselves aren't wrong per-se. How and when they are used or abused on the other hand can be seen as poor in terms of design and maintainability depending on the circumstances. :) – S.Robins Nov 27 '12 at 10:46
  • @S.Robins Think about the basics of OOP and modeling; objects are supposed to copy real-life entities. Are there real-life entities that have this types of operations/properties such as getters/setters? – Random42 Nov 27 '12 at 11:20
  • To avoid having too many comments here, I'll take this conversation into [this chat](http://chat.stackexchange.com/rooms/6530/programmers-watercooler) and address your comment there. – S.Robins Nov 27 '12 at 11:22