Hacker News new | past | comments | ask | show | jobs | submit login
Doing it wrong: getters and setters (typicalprogrammer.com)
25 points by gregjor on June 16, 2008 | hide | past | favorite | 26 comments



I can see what the author is trying to get at. If you are talking about building a library with an API, or maybe even a command line tool, then what he says has quite a bit of wisdom to it.

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.


I agree. In my past experience I have found it actually better to build all the domain elements (model) first based on command line usage, as it more clearly and accurately defines the problem (focus is not lost on GUI issues), and then work on GUI development.

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.


I'm not sure this excludes the use of getters and setters under any and all circumstances.

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.


Agreed. This article seems like a good idea (essentially: "don't write needless code", and "don't expose fine-grained stuff you don't have to") extended to the point of pedantry.

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.


I'm not sure that the examples you cite make the point you want to make.

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; }"


While the original article talked about accessors, the argument was really against publicly accessible properties on objects - even readonly properties. As demallien correctly argues, this approach breaks MVC.

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.


The author is mistaken. You should have no getters and setters of primitive data types. There should be no

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.


If you put everything important into the custID object, what is left for the customer object? Just a reference to its custID?

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.


I think you are calling "customer" what I'm calling "customer ID."

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.


Getters and Setters are ok in Java, but not in python. Java compilers are smart enough to optimize, and inline if a getter and setter has no side effects.

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.


I think the original post is suggesting that your pseudocode should look something like:

videoPlayer.play("myvideo.mpg")

videoPlayer.limitVolume(50)

I have to admit, in this kind of example, his argument is interesting.


ok, good point :) So, basically limitVolume is used to push the implementation of the that scenario up the food chain, to the player itself. How about when I want to set the volume exactly 50, depending on certain triggers.

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.


"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."

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.


Pesumably you might want to know what the current limit is too:

    videoPlayer.play(file)
    
    videoPlayer.setVolumeLimit(number)
    videoPlayer.getVolumeLimit()
    
    videoPlayer.setVolume(number)
    videoPlayer.getVolume()
Hmm...


Lol. Yeah, I thought about that too. :-) Although to be fair, you don't need the second pair of accessors now

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.


The article talks about having too many of these, period, which I certainly agree with: it's a bad sign.

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.


Yes, you shouldn't write getters and setters; your object system should do it for you. Do people really use programming languages without a proper metaobject protocol?


The author is suggesting that some getters and setters--particularly the ones that introduce a public method to replace a property against a private variable without introducing any actual value-added logic or protection--are counter-productive.

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.


In Java, exposing public instance variables of a class is risky because any changes to the internal operation of that class requires modification to the callers of that class. If you write your accessor methods up front then you can add additional behavior to those accesses at a later point in time. That is largely a syntactical issue.

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.)


I don't understand the objection here to getters and setters. Sometimes part of the public interface of an object is as simple as a property that can be read or written.

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.


> Sometimes part of the public interface of an object is as simple as a property that can be read or written.

I agree. In fact, I'm having trouble envisioning a program without any of these (or clunky workarounds to avoid them).


The purist side of me agrees with the author. I try to design my objects around behaviors and make them appear stateless to other objects and other developers.

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.


As an added bonus, sometimes the get() will do something more interesting in a subclass, like query a piece of hardware or a remote sensor. In that case you want the getter as interface so that the client can ignore the underlying complexity.


I'm not sold on this, but I can see what the author is trying to say. It would have been good for him to make a concrete argument rather than one based on theory.

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.


True. The author does not draw the line between using getters/setters and other methods to access data. I guess this is the motivation for ideas such as "leaky abstractions."


This whole discussion reminds me of http://www.pd.infn.it/~loreti/ootoaster.html




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: