« Kathy's zones of emotion | Main | Theory P: Software Development is Probabilistic »

November 16, 2007

Avoid Order Dependencies between Object Methods

My code was calling a certain method on a certain object. I was passing all the right parameters and the method was returning garbage. What the hell.

It turned out that I needed to call another method of the same object first.

An object that depends on its methods being called in a specific order is a bad idea. There's no language that I'm aware of that supports defining ordering dependencies for the methods of an object. If I make calls in the wrong order the compiler or runtime can't tell me I've made an error. I can't treat such an object as a black box. It's leaky. I'm forced to learn something about its internals. It defeats part of the point of encapsulation. Objects should be able to handle method calls in any order.

For the code I was writing I couldn't change the offending object. It's technical debt but it wasn't my call. The best I could do was add comments so the issue wouldn't be a hidden surprise for the next developer.

A minimal fix would have been to enforce the ordering constraint by having the method call fail if the prerequisite method hadn't been called. This is crude but effective.

Since this tactic implies keeping state in the object to track if the prerequisite method has been called, it might be nice to simply call the prerequisite if it hasn't been already. If the object has a method that is a prerequisite to a large number of other methods there would be this little bit of check code duplicated across all the dependent methods. That's a little ugly. And it's not DRY. (It's one of the reasons I don't like two stage construction.) So using object state as the principal means to solve the issue may not be the best solution.

A different approach might leverage the object design. Maybe these methods shouldn't all be on the same object. The 'prerequisite' method could be a factory method that returns an object that contains the other methods. If appropriate the factory could cache the last computed object.

In a well behaved object order dependencies among methods should be either designed out or encapsulated as an implementation detail.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/t/trackback/18301/23435376

Listed below are links to weblogs that reference Avoid Order Dependencies between Object Methods:

Comments

"There's no language..."

See CLOS.

>> There's no language that I'm aware of that supports defining ordering dependencies for the methods of an object.

How about a State Machine / State Pattern? That can determine what order methods are called in. What do you think?

I've dealt with this by writing my own little API to wrap the object, subclassing it, or monkeypatching it. The answer to all problems is another layer of indirection, right? Yeah, not really a bag of wonderful, but at least it keeps the ugly bits in one well-labeled place instead of speckled throughout my code.

I agree. It's amazing how many badly designed APIs there are out there that have this problem, like requiring a start() method to be called before publish() will work, yet never giving a hint as to why nothing happened when you called publish(). I recently got burned by that. Although I'm hesitant to turn it into dogma, this should be more or less common knowledge among developers.

Sometimes when an object represents some kind of external resource you end up with the situation you describe here. A classic example is files, first you open the file, then you process it, then the close() method should be called at the end. The same sort of thing is common when dealing with databases.

But if its just a regular object that isn't tied so something external to the VM then there should rarely be any reason for order dependencies. A constructor method should do everything thats needed to fully initialize an object and establish all its invariants.

Also known as "programming by accident", it's a clear sign that the two methods do not belong on the same class.

Ok... I'm missing something (or rather, I don't understand how come everyone else is missing it). Objects *are* state machines. You call methods to change their state. Of course invoke order matters, that's the whole point of having an object, if it didn't we'd have independent functions.

(As an example, of course the GetAge() method will return garbage if you forgot to call the SetDOB() method first. What else could it do?)

Marcel,
That's an interesting take on objects.

What else can GetAge() do? It can fail. Failing is better than returning garbage. In languages that support exceptions, GetAge() can fail by throwing an exception.

Objects can have state. If having state makes an object a state machine then that state machine should be constrained in certain ways. Required starting state should be created in the constructor and transitions should be allowed between all allowed states. Going back to your example, you could design an object where having no DOB value is not an allowed state.

Agreed. I'm trying to find a circumstance where it's better to return garbage and I can't find it :)

Yes, one reason for encapsulation, for private fields hidden behind methods, is to make sure that the object's state is always consistent. In a Stack class, we hide both the array and the stack pointer behind the Push/Pop methods, so that they're always in sync.

I was just objecting to the idea that call order shouldn't matter. It's an ambiguous statement - it can mean "methods should always return valid results or error out", as you apparently meant here, or "the return value of a method should not be influenced by what other methods have been called before". The second is true for Math.Sin(), but that's because Math isn't an object, it's a package where we put independent functions. It cannot (in general) be true for objects, we want the methods to influence the internal state *and* be influenced by it.

Marcel,
You're correct that I'm not saying that the state of an object shouldn't change. Of course the state can change.

And, yes, call order matters as to what the current state of the object is at any given point in time. But in terms of the semantics of the interface that an object presents, call order shouldn't matter.

Post a comment

Comments are moderated, and will not appear on this weblog until the author has approved them.

If you have a TypeKey or TypePad account, please Sign In

constructive nonconformist

Useful Books

*

  • Basecamp
    Basecamp project management and collaboration
  • Backpack
    Backpack: Get Organized and Collaborate
  • Technorati

Creative Commons

Blog powered by TypePad
Member since 02/2004