What Is Cohesion?
Yesterday we examined Coupling. Today we’ll look at Coupling’s alter-ego: Cohesion.
You’ve probably heard that code with low Cohesion is likely to be problematic, while highly cohesive systems are easier to maintain.
It’s true; but why? What is Cohesion?
I like this definition:
“A class has high Cohesion if it contains methods and data that ‘naturally belong together’.
Conversely, a class has low Cohesion if the methods and data don’t fit well together.“
Unfortunately, this definition is a bit wishy-washy; what does ‘naturally belongs together‘ actually mean?
OK, time for a couple of examples.
Firstly, say, we have a
Customer class in an eCommerce system.
Customer has the following methods:
Do these look like they belong together? No, they don’t. The responsibilities of the three methods vary wildly:
- Programmers from the IT department are responsible for data persistence. They change the implementation of the
PrintOrderReport()is an administrative function. The Accounting department uses it to track large customer orders. Accountants are requesting modifications to this report.
- Finally, Operations and Logistics drive changes to
Let’s get more precise with our definition of Cohesion. Robert C. Martin (aka Uncle Bob) has restated the Single Responsibility Principle, which is all about Cohesion, as:
“A module (or class) should be responsible to one and only one actor.”
‘Actor‘ is a term from Unified Modelling Language (UML) meaning ‘a distinct group of system users’.
Is our Customer class cohesive? No, since three separate actors may request changes to it:
- Logistics for
- the Accounting department for
- IT to the
OK, what about an example of a cohesive class? Class
ShoppingCart in the same eCommerce system has these methods and getter properties:
All five behaviours manage merely the contents of
ShoppingCart. A single ‘actor’ would request changes to the cart’s operations.
ShoppingCart is highly cohesive.
Finally, it is worth noting that we developers can reduce Cohesion even if all existing methods and data structures reside inside the one class. How would this happen? Simple—by not putting a new behaviour onto the class that it naturally belongs to.
To illustrate this point, we’ll continue with the
ShoppingCart example. Let’s say new legislation requires eCommerce websites to display the GST amount pre-checkout.
Programatically, one way to a high Cohesion implementation would be to put a
GstAmount getter property on the
Unfortunately, the developer didn’t realise that the GST calculation depends on a fixed GST rate and
ShoppingCart.Total and can therefore be encapsulated in
ShoppingCart. No, they decided to implement GST calculations outside of
ShoppingCart. In so doing, the developer has unwittingly dealt the eCommerce system a blow—Cohesion is now lower. This seemingly small mistake, left unchecked, will see us having code like this littering the application:
var gst = cart.Total * GST_RATE;
var gstAmt = cart.Total * 0.15;
while the more cohesive outcome might have looked like this:
var subtotal = cart.Subtotal; // before GST var gst = cart.GstAmount; // GST calculated inside ShoppingCart var total = cart.Total; // Subtotal + GstAmount
Carefully consider where you place behaviour—small mistakes today may blow up into maintainability headaches in the future.