Minor, Major and Overarching Design Principles
Design principles are guidelines about how to organize and structure our code to make it manageable.
They come through experience, in the general sense of the word. It can be one individual’s own trial and errors that makes them realize what options make code more simple.
But in the more general sense, design principles stem of our experience as an industry. They are the formalized results of decades of collective experience, as a community of software developers.
We’ve already been through the 9 must-know GRASP design principles exposed in the book Applying UML and Patterns:
- Information expert
- Creator
- Low coupling
- Protected variations
- Indirection
- Polymorphism
- High cohesion
- Pure fabrication
- Controller
You must know those 9 patterns to understand this article (and more importantly, you must know those 9 principles to improve the design of your code!).
However, those principles are not at the same level of abstraction (pun intended). Some of them are guidelines and some are merely ways to achieve those guidelines.
Our goal is to clarify the links between the design principles, in order to better understand them and better remember them.
Note: After writing this post, I realised that Craig Larman had also illustrated such relations in his book, for example on page 439 in the 3rd edition of the book. What’s interesting is that we didn’t get to the exact same results. If anything, this blog post lets us go through the reasonings by ourselves, and have a place (the comments section) where we can discuss about it.
One Principle to rule them all
Centuries ago, a spell was cast over all design principles, to bind them for eternity. The spell has been forged on an ancient piece of silicon, that was held and carried from generation to generation until this day.
Here is what the spell says:
Ash principluk durbatulûk, ash principluk gimbatul, ash principluk thrakatulûk, agh software-ishi krimpatul.
There is no exact equivalent in English, but the closer it gets is this:
One Principle to rule them all, One Principle to find them, One Principle to bring them all, and in the software bind them.
What is this One Principle then?
Although he makes no reference to the Lord of the Rings, this is what Steve McConnell explains in his classic (also absolute must-read) book Code Complete:
“Managing complexity is the most important technical topic in software development. In my view, it’s so important that Software’s Primary Technical Imperative has to be managing complexity. Complexity is not a new feature of software development.”
Why there is more than the One Ring Principle
The goal of managing complexity is good to know in order to understand the general direction in our job. But in practice, it needs come concrete complements. You can’t just hope for the complexity of a program to be manageable and everything will fall magically into place.
Managing complexity is such a high level principle that it is more a goal than a design principle.
That’s why other design principles come out of this overarching goal of managing complexity. They all seem to contribute, in one way or another, to making the complexity of software manageable.
This include the 9 GRASP principles. They are ways to handle complexity in our code.
So a rough way to represent the links between all those principles would be this:
The arrows represent a relationship that is close to inheritance: an is-a relationship. For example, the Information Expert principle is a way to manage complexity in code.
The relationships between principles
The above diagram is in fact very approximate. The principles also have an “is-a” relationship between each other. By exploring which principles extend which, we’ll get a better version of the above diagram.
Protected variations extends Low coupling
Protected variations consist in having a layer that prevents a part of the code to be affected by the changes in another part. This is almost the definition itself of low coupling. So Protected variations extends the principle of Low coupling.
Indirection extends Low coupling
Similarly to Protected variations, Indirection introduces a barrier between two parts of the code, so that the changes in either one don’t leak into the other. So Indirection, like Protected varations, extends Low coupling.
Note that, as we saw when exploring the 9 GRASP principles, Indirection and Protected variation are not quite the same thing: Protected variations is about creating a stable interface on a component, whereas Indirection is about creating a new component between other ones.
Polymorphism extends Low coupling
Polymorphism is about decoupling clients and implementations, by introducing an interface that can be implemented by various components.
With polymorphism, the calling code is independant from the various possible implementations, as well as from the choice of which implementation gets executed in a given context.
As a result, polymorphism is directly an extension of Low coupling.
Does High cohesion extend Low coupling?
In Applying UML and Patterns, Craig Larman explains that there is a link between High cohesion and Low coupling: Low Coupling is a side effect of High Cohesion. Later in the book, he describes cohesion and coupling as the “yin and yang of software engineering because of their interdependent influence”.
The link comes from the fact that a class that has too many responsibilities is probably connected to many scattered parts of the system in order to achieve those responsibilities.
For our classifying purposes, should we consider that High cohesion extends Low coupling? I don’t think so.
If you have an opinion on this, pro or against, please leave a comment for discussion below.
Information expert extends High cohesion
Information expert consists in assigning a responsibility to a class that has the data to carry it out.
I think it extends High cohesion, if we enrich a little the definition of High cohesion.
Strictly speaking, High cohesion means that a component does not contain more than one responsibility. But we can also expand this by saying that to achieve High cohesion, all of this responsibility must be contained in that component, and not be scattered in various (locally cohesive) parts of the system.
One aspect of its responsibility is to use its (consistent) data to achieve something. This is Information expert.
In that sense, Information expert extends High cohesion.
Creator extends High cohesion
Creator goes further than Information expert: it is a class that is close to another one that should take care of instantiating its objects. With “close” meaning: B contains or aggregates instances of A, or B closely uses A, or B has the inputs to construct A (like Information expert), and so on.
So Creator, like Information expert, extends High cohesion.
Pure fabrication extends High cohesion
Pure fabrication exists solely for the purpose of achieving High cohesion: if we have a responsibility that doesn’t map to any of the class representing domain concepts, then rather than burdening an existing class with this extra responsibility (and making it less cohesive), we create a new class for it. Even if this new class doesn’t correspond to something in the domain.
Pure fabrication is an extension of High cohesion.
Controller extends Pure fabrication
A typical example of Pure fabrication is Controller: it is a class that organises the reaction to a UI event, even if this concept doesn’t exist amongst the domain objects.
Controller is therefore an extension of Pure fabrication.
The resulting diagram
Put together, here are the relationships between principles that we have observed:
What do you think about those relationships? Please let me know your opinion in a comment below.
What about levels of abstractions?
I have already written (many times) that respecting levels of abstractions was an overarching principle in programming. Where should that fit in the diagram?
Also, it would be interesting to fit in some of the GoF design patterns. Some have a clear link with the design principles.
Those are topics to explore for future posts. If you have any inputs, please drop a comment!
You will also like
- GRASP: 9 Must-Know Design Principles for Code
- It all comes down to respecting levels of abstraction
- How to Insulate a Toxic Api from the Rest of Your Code
- To DRY or not to DRY?
- Functional Programming Is Not a Silver Bullet
- The Dangers of Coupling and How to Avoid Them
Share this post!