Friday, 3 May 2013

"Tell, Don't Ask" Object Oriented Design

This post has moved to http://coding-is-like-cooking.info/2013/05/tell-dont-ask-object-oriented-design/

4 comments:

holger krekel said...

Insightful,thanks.also helps me to reflect my test writing and design style.

Ralf Westphal - One Man Think Tank said...

I like the emphasis on message passing style.

But why does an object still need collaborators? Why still such dependencies?

That makes objects hard to specify: it´s 1+n interfaces, I´d say. 1 interface for the object/class itself, and n interfaces for classes it depends on.

If such an object (or function) then also has its own logic... that seems a lot of stuff going on.

Also these dependencies make it hard to test the object. Because you need to test two aspects at the same time: the logic the object is responsible for plus the correct interaction with any dependencies.

Emily Bache said...

Ralf - I think the normal case in an OO design is that an object has both logic and collaborators. You usually test different aspects in different test cases. Perhaps how this works will become clearer in my next post (which I'm working on) where I'll use an illustrative example.

Ralf Westphal - One Man Think Tank said...

I think so, too. That´s how it´s done traditionally.

But I´m thinking: should we continue doing it like this?

Because what it leads to is deep object graphs where logic (control structures and expressions) are smeared all over the place.

More and more I find this a violation of the single responsibility principle.

Consider this:

class C {
..private IS _s;
..public C(IS s) { _s = s; }

..public int F(int a) {
....var x = ... a ...;
....var y = _s.G(x);
....return ... y ...;
..}
}

One could say F() has a single responsibility which uses function G() on some interface IS.

So there should be only one reason for F() to change: if and only if some aspect of its responsibility changes. It might be, how x is calculated, changes.

But that´s not true. There are more reasons for F() to change. Here´s one:

If by application of the Interface Segregation Principle G() is moved to another interface, then F() as well as C have to change, too.

So there is not only a dependency on some "G semantics" (or IS service for that matter) but also on where (!) this semantic is located in the environment of C. There is logical as well as topological coupling.

That, to me, is opposing true message passing style programming. Because when I send a message (like an email or a SMS) I don´t care where the recipient is located right now.