September 6, 2010

...Learn TDD with Codemanship

"Ah, But What About Interface Segregation?" I Hear you Cry

Ah, but what about Interface Segregation, I hear you cry?

Yes, it's true. There is an OO design principle that tells us that objects should present client-specific interfaces. So if a Woman plays the role of Mother with respect to her Daughter (who is also a Woman) and a Boss with respect to her Employee and an Author with respect to her Book then we might want to have Woman implement a Mother interface for the maternal responsibilities, a Daughter interface for the other end of that relationship, a Boss interface for barking orders and an Author interface for collecting her royalties.

But didn't I say that interfaces should be implemented more than once? Nope. What I said was that if we follow a process of discovering abstractions during refactoring than we might expect to find that interfaces are implemented more than once. A nitpicking point I know. But it's important to clarify that one is the goal (discovery of abstractions) and the other is an indicator.

But even then, it's absolutely true that on this point (and this point alone) I'm not aligned with the GOOS school of OO design. I tend to feel that an object that's playing multiple roles often turns out to be breaking another OO principle - namely that classes should only have one reason to change.

It all gets a bit handwavy and vague here, I'm afraid. Which is probably why the debate goes round in circles. Truth is, we just don't know. Not for sure. In OO design, we tend to find that there's no principle or rule of thumb that's always true in all cases.

Firstly, Steve and Nat aren't saying something as simplistic as "program to interfaces". If that were true then their approach wouldn't work in, say, PHP. Just as I'm not saying "interfaces should be implemented more than once". They're saying that objects should collaborate with roles (Mother, Daughter, Boss, Author) not types (Woman). They just so happen to model roles using interfaces in their chosen languages. From a dependency management standpoint, that's good advice. "Program to interfaces", on the other hand, is not. Which is why they don't say it. Even if that's what some people choose to take away from what they actually do say, just as some might choose to take away "implement interfaces more than once" from what I say, instead of "introduce abstractions when you see multiple examples of something during refactoring". And as Kent Beck rightly points out, even that is oversimplifying it.

You will find in my code classes that have multiple responsibilities and perform multiple roles, and - gasp! - have multiple interfaces. But in my code they are probably rarer than you might find in other people's code.

Here's how I slice it and dice it: if a class plays multiple roles that all relate to the same data, then splitting that class up into multiple role-specific classes would introduce higher coupling, since the methods on those new classes would have to refer back to the fields of the original. Feature envy, basically.

On the other had, if we had loosely coupled clusters of methods and related fields for each of those roles inside that original class, I'd probably split the class up.

In both schools, we favour organising our code by roles and responsibilities. The Reused Abstractions Principle (RAP) is a counterweight to balance out the tendency to put interfaces on everything which is increasingly widespread. So when I teach OO I throw it in there as a "please don't go mad with the interfaces" kind of appeal, and also to give extra weight to the discipline of discovering rather than inventing abstractions. Because I'm really a feedback-driven design kind of a cat.

I may have a lower tolerance for interfaces than some. And that's based on my subjective experience, is all. Those who apply them more liberally also do so based on their subjective experience.

And that's fine. Because OO design is an art - even though there's some science underpinning it. Y'know. Some people can't listen to anything that isn't in 4/4 time. As a coder, I'm wired differently to many other coders.

The ultimate arbiter is the quality of the end result. So I would always - after hours of handwaving and telling people what I "like" in a design - encourage people to collect some more objective evidence by analysing the dependencies in their code and seeing if, overall, things are better or worse in the dimensions of quality that interest us, like coupling and cohesion. My confidence in my approach comes from that, but I know there will always be room for improvement and that other approaches can achieve similar results.

What I have less time for is overly simplistic design formulas and "rules of thumb". To their credit, and there's a lot to credit them for, the GOOS guys don't fall into this trap. Their approach is nuanced and they give ample room for skill and judgement - things that take years of practice to develop.

The danger with any approach is that inexperienced developers might choose just to take away the simpler truisms and apply them religiously as the "formula for good design". In reality, there's nothing we can do to stop that, and some developer communities seem more inclined this way than others. RAP has always been an attempt to balance some of these formulas, and should not be considered an OO design principle of the first order.

Now, some people argue (with some merit) that we've all got to start somewhere, and that these formulas are useful for novice OO designers. I would be more open to that idea if there was such a thing as "novice OO design". I can teach a novice chef to boil an egg. Software, in practice, starts challenging and goes downhill from there. You can't turn a fiendishly difficult ski slope into a "novice" ski slope just by telling people "try to keep to the left" before you send them hurtling down. So I avoid the truisms whenever possible and try to focus more on the underlying mechanics of OO design - which is the mechanics of dependencies - right from the start. The rest is a process of helping them to build up their experience in applying these basic principles, which is where the real knowledge comes from.

Indeed, it's very hard to discuss any aspect of this complex discpline without unwittingly introducing "certainties", just by dint of the way language works. The most meaningful way to make a point is using examples. And it takes a lot of examples to really get a grip on OO design. Hundreds, possibly thousands. And then we develop a knack for it. And the trouble with knacks is they are nigh on impossible to communicate with any fidelity. If you don't believe me, try teaching someone to ride a bicycle using PowerPoint slides.

Crikey. Now it's definitely time for some lunch.

Posted 10 years, 6 months ago on September 6, 2010