Página 5
Manual de Estilo de Programación
Página 6
Índice
See this article in english 
Página 7
Comments on JSR 315 (Servlets 3.0)

Lo sentimos - la página solicitada no está disponible en castellano. Mostrando la versión : en

About interface abuse, DI-infestation and interfaceitis

Alexander Hristov

 

08 - Mar - 2008

I recall one of my first copies of Dune (the book). I was excited to get it, especially because in my country, at that time, science fiction wasn't widely published and there was a scarcity of western authors, and Dune was one of the most talked-about books. So I started to avidly read it, page after another. As I read, I became more and more disgusted, until after some fifty pages or so, I threw the book in anger, never to tuch it again.

Why?

It wasn't because of the story, for sure. The problem was something completely unrelated. Frank Herbert used a lot of invented terminology in Dune - invented words for just about everything, and used them freely in his book. Since it's difficult to understand at the beginning what's going on without knowing what those words mean, the editors of the book "brilliantly" decided to put an alphabetic dictionary of terms at the end of the book, effectively transforming the pleasure of reading into a nightmare routine of "read two sentences - go to the dictionary and spend half a minute looking for the words". A simple footnote or margin annotation would have solved the issue.

Continuity is important for reading

Why am I talking about this? Every now and then, I come across a project that I consider as so notoriously misimplemented that makes me want to scream. This project I have in mind now was a severe case of what I call "Interface-itis".

An interfaceitis-struck person will keep writing interfaces even when no one could ever possibly even dream of using an alternative implementation of any of the classes there. So this kind of person, instead of just having a Cat eat() some Food, will have instead an ICat, then a Cat implementing ICat, then an IConsumable, an IFood extending IConsumable, and finally a Food implementing IFood.

But wait... it gets worse! In this particular project, both Cats and Foods had to be persisted, so why not bring some anemic domain model antipattern, too? So we end up with the corresponding DAOs of course, god forbid that we couple ourselves to some relational data store!. So there I was, trying to juggle not only Cats and ICats, but also CatDAOs and their corresponding ICatDAOs, of course. And just when you think it can't get worse.... it does! Because under the DAOs there was... an ORM layer. And all of this ill concieved flexibility was for accessing a corporate database upon which literally dozens of other projects depend and whose chance of changing significantly was all but nil.

And then, do you think the interfaceitis guy will be happy with ICat cat = new Cat()? No way, sir!. He'll instead just declare cat to be an object field of sorts, then use an external dependancy injection system to put a value inside, and then do this for every single nontrivial field.

Interfaceitis is usually worse in the Spring, where you end with either huge wiring configuration files, or with an infestation of micro Spring-XMLeaves, in which one Spring XML configuration file loads 5 more, each of them in turn loads 4 more, and so on and so forth, ending in a situation where every time you just want to read a piece of code, you have to look up where in hell the property in front of your eyes has been wired to some specific implementation. And you can only pray that the dependancy is injected statically, as opposed to some wierd rules about what gets assigned where. Of course, you can run it and inspect the value, but :

There's something very wrong with a project if it's easier to follow by running it than by reading it.

Of course, the problem is not Spring per se, the problem is in the Dependancy Injection pattern, which - like every pattern - has some specific use cases for which it was created. It's not by chance that the mother of all pattern books had a specific section called "Applicability" for each and every pattern. But nowadays many people I've met seem to pay little attention to this specific issue. It's more like "hey, DI is cool, let's use it for this project". And they go on and use it in every single miserable class.

Christopher Alexander defined a pattern as "the description of a solution to a problem which ouccurs over and over again in an environment". The "solution to a problem" part is important : patterns are applicable in specific situations, and they are applied to solve a problem that exists beforehand, not just because they are cool or nice.

Some people often confuse the rule of "write code to interfaces and not to implementations" as meaning literally "write interfaces and code to them". Well, this is nonsense (not the rule; the sloppy interpretation). It is as nonsensical as taking the rule "use structured storage" to mean "use struct {} constructs".

"Write to interface and not to implementation" simply means that you should use only and exclusively the published contract of a part (be it a method, a subsystem, a file format definition, whatever). That contract can be expressed in a variety of ways : a method signature in either an interface or a class , a formal definition like a WSDL, a document, wherever. The rule does not imply in any way using or abusing a specific programming language idiom. Actually, the GoF books talks about defining the contract in an abstract class and inheriting the implementations from it.

A person obsessed with writing interfaces for every tiny bit of functionality is like a person that insists in writing (and maintaining) a WSDL for each of his classes. Because, after all, WSDLs are much more powerful in expressing method contracts than method signatures. So go on, laugh off your head at this idea, but think about it next time you start a file with public interface .

As Steve McConnell puts it, programmers are notoriously bad at predicting how user requirements and execution environments will evolve. Unless you are writing a framework, writing interfaces for "just in case" hypothetical scenarios is a dangerous road. Again, next time you're tempted to do it, consider whether you were successful in predicting the need for a particular kind of flexibility in your past projects.

Not every field needs a getter and setter

Interfaceitis is similar to the virus that turns people into getter/setter zombies : they write a class, write some state fields, and then immediately proceed to generate every imaginable getter/setter without giving much brains or though about whether those getters/setters are really needed. These people open operate in what I call "auto-pilot" mode : they just play a pre-recorded sequence of fingerstrokes in their lower brains, and not even a divine intervention would stop them.

Remember that the central point of most complex objectes is to provide a service. Not a chunk of data, but a service. Internal state plays only a supportive role, and should be as hidden as possible. Its even existance should be hidden, if possible. The getter/setter zombies often forget that the state of an object is the state OF that object. the second "OF" meaning "belonging to me", "mine" or "get your hands outa my stuff or I'll blow off your head".

"Code should be easy to test" does not mean "coding crap is ok as long as tests are nice"

A notorious source of interfaceitis infections are unit tests. On the altar of unit-testing, sacrificing everything else seems fine : readability, efficiency, maintainability, codebase size and what not. Unit tests serve their purpose of course, but they are just one facet of a quality project. One of the guidelines of unit testing has been "write code so that it is easy to test". But unfortunately, this is also too often taken literally, and the resulting code is a complete abomination where everything is public (so that it can be tested), everything has an interface and interacts with everything else through interfaces (so that they can be mocked), and so forth.

Base code is the primary objective of a project. Unit tests are only part of the scaffolding that makes sure that things are built correctly, that work properly and according to some expectations ("requirements"), and that when evolving the system one doesn't break anything.

But there are other targets to meet, too : For example, it's imperative that base code should be readable, so that new people arriving at the project don't have to digest a huge amount of XML before understanding what's going on.

If your only justification for introducing an interface is allow for an easier unit testing, then you have no justification. There are perfectly valid and completely system-transparent ways to inject mock objects into the base system (for example, via reflection) that do not require an interface-plagued system.

Complexity is the one of the most, if not the most important danger in any project

Human beings have a limited cognitive capacity. Every irrelevant line of code, every "just in case" interface, every superfluous variable declaration, every unneeded pattern, every bit of unnecessary flexibility in the form of injections, indirections and added layers is a step towards the grave. Life is already complex enough. There's no need to self-inflict even more complexity to it.

 

 

Comentarios

28/03/2008 a las 14:59 Enviado por Alexander
Mwanji: Sure, but that doesn't mean that they must be public. I don't know about Struts 2, but for Hibernate, your getters/setters may perfectly well be protected, for example.
28/03/2008 a las 13:02 Enviado por Mwanji Ezana
I've been coming around to a lot of the ideas you express here. One problem with getters/setters is that a lot of frameworks kind of force you to have more of them than you'd like (in our case, Hibernate 3 and Struts 2).
28/03/2008 a las 01:55 Enviado por anonymous
Great post!

 

Añadir Comentario

Nombre (opcional)
EMail (opcional, no se muestra)

Texto