#28: Cohesion
Hey there! Today, let’s talk about the concept of cohesion.
Cohesion measures how well the elements inside an aggregate belong together. Said differently, it defines the strength of the relationships between elements within this aggregate.
NOTE: We used the generic term aggregate here, which refers in the context of this issue to something composed of multiple parts. For example: a module composed of multiple classes, a class composed of multiple methods, or even a function composed of multiple instructions.
Consider a bedroom. If we arrange our bedroom to put a bed, a cupboard for storing cutlery, and our current bottle of shampoo, there is a clear issue. These elements don’t serve related purposes—one is for sleeping, one for eating, and one for washing. This setup lacks cohesion, which we call low cohesion.
Now, let’s rearrange our bedroom so that it contains a bed, a wardrobe for clothes, and an alarm clock. These items all relate to the purpose of the room—resting and getting ready in the morning. This room is said to be cohesive or to have high cohesion, as everything works together for a common goal.
Back to software engineering, if we take the example of a class, it means that all the elements inside this class (like methods and constants) should all focus on the same responsibility. All the elements should be related to a single intention or task.
Why should we aim for high cohesion?
Readability: We mentioned readability this week; cohesive code is easier to understand. We can quickly grasp what an aggregate1 is meant to do.
Maintainability: When fixing bugs or adding features, cohesive aggregates make it easier to identify what needs to change.
Testability: Cohesive aggregates are simpler to test because their behavior is focused and isolated from other concerns.
Reusability: A cohesive aggregate with high cohesion can be reused more easily because it has a clear, single purpose.
Here are some smells showing our code lacks cohesion:
Shotgun surgery: When changing data or modifying a behavior requires changes across multiple aggregates, it indicates (generally) low cohesion. For example, changing a behavior in aggregate A forces us to make changes in several other classes:
God object: An aggregate with too much responsibility, or that references too many distinct types:
Middle man: An aggregate that simply passes tasks to other aggregate(s) without adding value:
There are different types of cohesion, from most to least desirable:
Functional: Elements work together to perform a single task. This is the ideal.
Example: A
CoffeeOrder
class containing methods likeaddItem
,removeItem
,calculateTotalPrice
, andcheckout
. All methods serve the same purpose: managing a coffee order.
Sequential: Elements are related by a sequence, where the output of one becomes the input of another.
Example: A
DataProcessor
class where methods need to be called in a specific order, likereadData
,processData
, andsaveData
.
Communicational: Elements operate on the same data.
Example: A
UserProfile
class with methods that focus on the same user data likegetUserDetails
,updateUserDetails
, andlogUserActivity
.
Procedural: Elements perform related tasks but don’t need to be executed in a specific order.
Example: A
DocumentValidator
class with methods that all check different aspects of a document, but they can be executed in any order likecheckSpelling
,checkGrammar
, andcheckFormatting
.
Temporal: Elements are grouped together because they are executed at the same time, even if they serve different purposes.
Example: An ApplicationSetup class with methods like
initializeDatabase
,loadConfig
, andstartLogging
, all of which are called when the application starts up.
Logical: Elements are grouped because they fall into the same category but serve different purposes.
Example: A
Utility
class that contains helper methods likereadFile
,sendEmail
, andparseJSON
. All of these are utilities, but they serve different purposes.
Coincidental: The lowest form of cohesion, where elements are grouped arbitrarily and don’t have a meaningful relationship.
Example: A
Miscellaneous
class with methods likeplayMusic
,calculateTax
, andprintInvoice
. These methods are unrelated and just happen to be in the same class.
Cohesion is essential for keeping our code in good shape. By aiming for high cohesion, we ensure that each part of the code has a clear purpose, making it easier to understand, maintain, and work with.
As we discussed cohesion today, it naturally leads to tomorrow’s topic: coupling.
Again, this could apply to a module, a class, or a function, for example.