It seems that type/class hierarchies in OOP may be designed in two extreme ways: either with full encapsulation of data in mind; or with just a few interfaces making raw data visible, and letting classes deal with it, parse it, and turn it into smaller data elements. You may be surprised, but I’m suggesting the second option is more elegant. It seems to me that we don’t lose object orientation, but rather gain a lot of flexibility, reusability, testability, and so on.
Take a look at this (let’s call it fat and I will explain why later):
To obtain the name of the author we do:
Visually, this design may look like this (in UML):
Can’t render PlantUML diagram.
Now, let’s compare it with an alternative design (which is much less fat than the previous one, I would even call it skinny):
Here, in order to obtain the name of the author we have to extract the head as a
String, extract the author as a
String, and then extract the name as a
Article a =
String head = a.head();
String author =
String name =
Visually in UML, it looks like this:
Can’t render PlantUML diagram.
There were three interfaces in the first design, while the second one has only one interface and two classes. I call the first one “fat” because it returns interfaces, which already implement the functionality we are looking for, and we don’t need to cover them with additional decorators or adapters. Its hierarchy of three interfaces is rich enough to give us everything we need. That’s why it’s fat. The second one, on other hand is pretty skinny, there is only one interface, which returns us plain text data, which we have to parse on our own. We need to dress it up.
It seems that the skinny design is better, for a number of reasons:
- Extendability. The skinny design is definitely easier to extend. In order to extract some new information from the author we just need to add a new method to the class
TxtAuthor. We don’t need to re-design the entire hierarchy of interfaces and to modify all their implementations. We deal with pure data, which is managed and parsed later, in decorators, adapters, and other supplementary smart classes.
- Cohesion. The skinny design is definitely more cohesive, since everything that is related to the management of PostgreSQL data stays in one class
SqlArticle. To the contrary, the fat design spreads the functionality among many classes and, thanks to that, makes the entire class-set more difficult to maintain.
- Reusability. Class
TxtAuthorcan definitely be used in any other place, where parsing of an author’s information is required, while class
PgAuthoris only suitable for one particular case: fetching and parsing PostgreSQL-related data.
- Testability. Obviously, the skinny design is much easier to test, because mocking a single interface is a much simpler task than mocking the entire hierarchy. To test the class
TxtAuthorwe just pass some fake text to its constructor and check how it works. To test the class
PgAuthorwe would need to do much more, including running a fake instance of a PostgreSQL server.
Everything said above is true for both 1) from-PostgreSQL data retrievals and 2) to-PostgreSQL data manipulations. Of course, manipulations may require many methods to exist in
SqlArticle, which will make the skinny design look ugly, and it will become obvious that some of these methods have to be moved to lower-level classes/interfaces. This only demonstrates that it’s not always possible to make skinny design with a single interface, like in the example above. Sometimes we simply have to make it more fat.
However, there is one serious concern related to the skinny design: it lets the raw naked data jump out of
SqlArticle, which is against the very idea of object-oriented programming, as we know. Indeed, if we let
TxtHead do the parsing, we may lose some interesting PostgreSQL-related context, which is available only inside
SqlArticle. We don’t want complex data parsing to happen far away from the place where the data is born. We want everything data-related to happen where the data lives: inside
This is a valid concern, but letting PostgreSQL-related information (like connection settings) move from
PgHead and then to
PgAuthor is an even larger violation of the data encapsulation principle.
In real-life situations, of course, it’s not possible to imagine pure one-interface skinny designs. They will all be fat to some extent. My suggestion, though, is to try to make designs less fat, letting interface users dress them up however they like. This suggestion is very close to what I said earlier about smart classes, but this time the principle is broader.