Tell, dont’t ask (and why getters and setters can be dangerous)

Tell, don’t ask. In every other situation in our lives, we have it drilled into us that this is rude. Polite people ask for things, they don’t demand or give orders. Yet, in OO design, the situation is reversed and we are actively encouraged to boss our objects around. In OO design, we get given a Thing, and we want to tell that Thing to doSomething(). We don’t care how (within reason), we don’t care what it needs to store or calculate and we don’t care where it stores it. We just care that it does something, and the results are what we expected. So why is this a good thing?

This is all part of encapsulation – hiding the internals of an object. By always telling and not asking, we are divesting ourselves of the responsibility of knowing about the object itself. We have it, we know it can do what we want. That’s enough for us. This leads to other interesting considerations regarding inheritance. If we have an ActingObject which can doSomething(), we don’t care if its a Thing or AnotherThing. We only care that it can doSomething(). By only telling the object what to do, Thing and AnotherThing can accomplish the same task any way they like, with no interference. We can refactor to our hearts content without breaking external objects.

The traditional way to explain things like this is in terms of cars. My knowledge of the internals of cars is almost as limited as my knowledge of how to drive the things, but I’ll give it my best shot:
When we start a car, we don’t ask the ignition to ask the engine to tell the spark plug to fire. We tell the car to start. Below are two equivalent ways to start a car:

car.getIgnition().getEngine().getSparkPlug().fire();
car.start()

Which looks nicer? Which relies on us knowing about how a car works?

Now consider putting in a car with an electric engine, which doesn’t use a spark plug. We change our methods like so:

car.getIgnition().getEngine().start()
car.start()

But wait, only one of those lines changed!

In the second example, by not assuming anything about how our car is structured, we don’t need to change any code external to the car. Obviously, the car needs to know about its engine and the engine needs to know how to start itself, but we don’t need to know anything other than that the car starts. Each object has only a single concern and delegates its responsibilities by telling the appropriate sub-objects what to do.

Another way I’ve heard this expressed is “Don’t talk to strangers“. An object shouldn’t need to talk to objects it initially knows nothing about (in the above example, we knew nothing about the ignition until we explicitly retrieved it from the car). This is formalised in the Law of Demeter:

A method M of an object O should only call methods belonging to:

  1. O
  2. O’s directly accessible fields
  3. Any parameters of M
  4. Any objects created by M

The advantage to this approach is that it helps to produce loosely coupled code. By assuming very little about the objects we use, we have a much easier time changing those objects without breaking external code.

The disadvantage is that we find ourselves writing lots of wrapper methods to facilitate the commands that could be given to the object. For example, in the car class:

public void start() {
  this.engine.start();
}

In the engine:

public void start() {
  this.sparkPlug.fire();
}

There are some additional rules as extensions to Demeter, which I think also make sense (especially from a testing point of view):

  1. Whenever an object needs to request a service of some other external object, this external service request should be encapsulated in an internal non-public method of the object. This allows derived classes to override the service request with a more specialized one
  2. Whenever an object needs to instantiate some other externally associated object, it should do so using a non-public method to perform the instantiation. This allows derived classes to instantiate a more specialized object if needed.

So why are getters and setters dangerous?

I had a conversation with a developer far more knowledgeable than myself a few days ago. He told me that getters and setters were evil. His reasoning (and the reasoning of articles I’ve read since) was:

  1. As far as possible, we want object state in our programs to be immutable. This makes it easier to be confident that an object will behave the same way throughout its lifetime. Having setter methods changing things directly, or getter methods exposing internal fields to modification, undermines this confidence.
  2. Getters and setters break encapsulation. They expose details of the implementation of our objects

I completely agree with that, and having tried to write code without getters and setters for the past few days, I actually quite like the idea. However, I still can’t shake the feeling that getters and setters may have their place – for instance, when the full extent of an object’s state is not known when it is created, or when creating a very generic API (the JDBC API, for example).

Therefore, my version of this assertion is “Getters and setters can be evil, and are at best dangerous“. Maybe I’m not seeing the full picture yet, and I may come back and change my mind on this later.

References:

  1. http://pragprog.com/articles/tell-dont-ask
  2. http://www.cmcrossroads.com/bradapp/docs/demeter-intro.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s