Doing it wrong: getters and setters
typicalprogrammer.com
typicalprogrammer.com
Unfortunately for the author, industry's long experience with GUI apps indicates that MVC is the way to go - when I consider the frameworks that I use for writing UIs, the two that most clearly give me an MVC architecture (Cocoa and Rails), are easily the most productive frameworks that I use.
This comes into conflict with the original article when you consider the M of MVC. A Model makes no sense at all without getters and setters. It's job is mostly to handle serialisation of data - either to a database using ORM (ActiveRecord), or to a file using something like Cocoa's NSCoding protocol. It may even be used for serialising data to be sent over the network.
Regardless, if the UI can't get it's hands on the data, the data is useless. So how can we get the data to the UI. If you look in the comments section of the article, the autor suggests (in responding to a question for how to populate a pull-down menu list) that you create a method in the model class that takes a pull-down menu as a parameter, and then populates the menu. No! Models should never even know what a pull-down menu is! How can I re-use my model code elsewhere if it is tied to a GUI.
Again, going back to ActiveRecord. I can create a model class inheriting from ActiveRecord, and re-use exactly the same class in a RubyCocoa app, or a Rails app. How am I supposed to do that if my model class starts spitting out html, or starts calling NSArrayControllers?
No. If the author is right, then MVC must be wrong, and quite frankly I don't think that he has presented a strong enough case to justify one of the more successful architectural choices that we as an industry have found over the last 50 odd years.
This allows very loosely coupled yet highly cohesive systems to be easily built. The main problem I face with others who adopt a GUI-driven development method (which has many merits of course) is they start to confuse elements/logic/tiers and tie things together too much at too early a stage and hence is is no separation of concerns and elements making systems harder to change/scale/iterate-upon etc.
A purist argument is that objects are supposed to do stuff, and that methods should be big, chunky verbs that don't expose unnecessary implementation details - including dozens of properties and twiddly knobs that "could be useful some day".
But if you're building container classes for a model, then your value of "do stuff" is "store data and make it available to others", so the use of getters and setters seems entirely reasonable.
Certainly accessor methods can be clean and useful, especially for things like computed or read-only values. They certainly aren't a red flag for maintainability by themselves.
Objective C 2.0 just introduced properties. One of the main reasons for this is to avoid manually writing accessors.
I'm pretty sure that in Rails, you just give an annotation (I probably got the terminology wrong here) indicating what attributes you want to make accessible. Again, no manually written accessors.
I suppose this is different from the argument made by the article, but it is still an argument against WRITING accessor methods. Whatever benefits there are for having some kind of accessors mechanism, certainly there is no good reason for having to explicitly write methods to implement them. I never saw the point in writing "private int theNumber" immediately followed by "public int getTheNumber() { return theNumber; } public void setTheNumber(int n) { theNumber = n; }"
It's not stated outright, but the original author obviously has a Java or C++ background. The Java & C++ syntaxes for accessing a getter are different from those for accessing a public member, and as a result Java/C++ programmers almost always write accessors for all properties because it will be hard to change later.
private String custName;
public String get custName(){...}
public set custName(String cn){...}
The way to handle this situation is to make a custID object that contains custName, and allows you to add custAvatar, custNumber, etc., as needs change and grow. Then it's ok to have getters and setters for the custID object.
I often find that my "data" objects (custID in the above) have all public variables (that is, custName is public, custID is public, etc.). I find it preposterous to have private variables and public getters and setters that do no "processing" of the input.
The reason for encapsulating this way is that if, two years later, you find your needs change and you need other information attached to the "customer," you don't break your old contracts. It's easy to add data to the data object for the objects that now need them, and for the others to ignore it.
And how does a custID object help you to avoid breaking old contracts? Adding stuff directly to the customer object wouldn't break your old contracts either.
What could break your contracts however, is omitting getters and setters and just making variables public. What if you have to add some "processing" of an input later? If you have had a setter from the beginning, it can be done transparently without changing any classes that use the setter.
You then add getters and setters for the customer (custID) object. Any pre-processing is handled there. Also, some vars in the custID can certainly be handled with get/set--vars set via user input, for instance.
But I also use this for building, for example, custom buttons. Things like color, stroke, etc. all set in a "buttonData" object. My custom button comes from a class that extends JButton with a constructor that accepts the buttonData object.
The beauty of this is that I can add a new feature to my buttons, say transparency, but (i) modifying the new Button class to paint transparently, and (ii) adding the transparency value to the buttonData objects. Nothing else in the code has to change, and I have custom transparency.
If I want a particular set of buttons to have a unique transparency, say the "help" buttons, I simply vary the value of the buttonData object before calling that constructor.
This is the power of OOP: encapsulating what changes. Is it slower to build? Absolutely. But you can probably already see how simple it is to modify and maintain.
But while it is good to avoid them, sometimes it is impossible.
Consider this pseucode, a simple scenario, when starting a video, if the default volume is too loud, I want to reduce it somewhere in the middle.:
....
videoPlayer.play("myvideo.mpg")
int vol = videoPlayer.getVolume()
if ( vol > 50 ) {
videoPlayer.setVolume(50)
}Getters and setters make perfect sense.
Avoiding them is good, but I really don't trust people that scream "Getters and setters are evil". No, they are not. They have their place, but as everything they should not be overused. But in terms of bad software design, if somebody overused them and exposed things should be exposed, they are pretty begnin, not even close to Dr. Evil evil, more like mini-me evil.
videoPlayer.play("myvideo.mpg")
videoPlayer.limitVolume(50)
I have to admit, in this kind of example, his argument is interesting.
What if I have no control on the videoPlayer, (third party), and now I am limited only on the scenarios they thought were ok to implement?
I guess, that's the point of the author, to enforce encapsulation, but by limiting the actions to only "verbs", the author is restricing the user of that object to what he thinks he should be doing.
Basically, the author is limiting my ability to do things the author didn't thought I needed.
It is like pushing implementation up, and, limiting what users can do. It will probably be a system with less bugs, but also a cripled one.
Yes, this is definitely one of the tenants of (at least some definitions of) Object Oriented programming. Arguments for this include giving an object the ability to ensure some kind of contract is maintained, which becomes impossible if you can arbitrarily change certain values without making sure that other values are also updated appropriately. Making data private and making all changes only through public methods makes it possible to guarantee that data inside the object remains in a consistent state.
You may not find this a convincing argument, but it is a widely accepted part of OO methodology.
videoPlayer.play(file)
videoPlayer.setVolumeLimit(number)
videoPlayer.getVolumeLimit()
videoPlayer.setVolume(number)
videoPlayer.getVolume()
Hmm...That said, I can only think of one use case for wanting to know what the current limit is - so that I can restore it when I'm finished. So maybe the API needs to look like this:
vp.limitVolume() vp.saveLimit(self) vp.restoreLimit(self)
Anyway, I'm not disagreeing with you really. At the end of the day I think that accessors are necessary and even desirable (see my other post about MVC). Technologies such as Cocoa bindings can massively reduce the amount that needs to be written by using accessors.
But if they must exist, you should have a facility for doing them in an automatic way. If you don't, you are better off keeping data members public.
Of course, I really like how Python properties handle this, because the syntax is the same as if the accessor methods never existed.
I've been pondering the following things recently in relation to the glue code in Java issue:
1) IDEs often require Beans be created, which require getters and setters. So, if you have to use IDEs, you are pushed towards getters and setters in many cases to create Bean-compatible classes. 2) Rather than stuffing all this in the code and creating explicit glue, would it be possible to implement an interface which adds getters and setters for Bean compatibility at runtime if explicit ones have not been created (ostensibly because there is no value to add in the method body that provides additional logic or security)?
I need to get back in to this and see if its possible or even makes sense. I've been out of Java since 2000.
A similar reasoning causes the proliferation of interfaces and factories. The inability to dynamically create objects at run-time leads to a great deal of indirection created at write-time and compile time. That's a semantic issue which creates tons of complexity for little gain. Beans are a direct response to these limitations. For instance with most dependency injection frameworks you can alter an XML file to rewire Bean creation without having to recompile or modify the source of your Java codebase. It's a hack emulating a subset of the capabilities of dynamic metaprogramming.
The Bean style of operation greatly encourages all of these verbose design styles. Some of it can be avoided through introspection, aspects, and annotations, but that is very clumsy in Java. Java practices have indeed changed a lot since 2000 but the increase in incidental complexity is just stunning.
(major edits: expound upon my points.)
Is the idea just that a large number of getters and setters are a good indicator of poorly chosen encapsulation boundaries? I haven't seen code go wrong that way.
I agree. In fact, I'm having trouble envisioning a program without any of these (or clunky workarounds to avoid them).
But sometimes you just need a data cow, and when the language makes you use objects you don't have much choice but to get/set.
For instance, on several projects I've had code that used getters/setters all over the place. But there was no direct access to private variables. When I was debugging and had a private variable in a bad state, I just set the break point on the setter -- caught the bug every time.
Now maybe I'm out of whack, but I'd like to see another concrete example on the other side. I get the whole noun/verb deal -- heck, I teach it. But what I'm interested in is how exactly Object.Property breaks encapsulation? After all, the caller has no idea how the underlying storage/retrieval is executed.
There is a minimalist philosophy here -- only do what you have to do. I think that's the best argument he has. But he didn't specifically call it out.