Single Responsibility Principle in Object Oriented Design


The story continues …

Mohan was sitting ideally in his room, waiting for his next task to be assigned after promotion. Soon Rahul came to his office,

Rahul: Congratulation on your promotion Mohan!
Mohan: Thanks Rahul.
Rahul: As you already know, the US team is moving on to another project. So now it’s our responsibility to sunset their current project, ‘The Dosa Maker’, and come up with an innovative design for the brand new ‘The Dosa Maker V2.0’. *Mohan: *In that case, why do we need a V2 ? can’t we use the same code here?
Rahul: Stop talking nonsense, If we don’t duplicate, then who will pay for your salary?

Mohan: Got it, I will do my best!


This is how the Dosa Maker Modules looks like

The Wise Developer: Do you see any problem with this?
Mohan: It’s a mess! Why a single class is handling so many responsibilities?
The Wise Developer: Good question Mohan, seems like that promotion wasn’t a fluke!

A more well-known term for such type of class is God Class. As the class seems to be doing everything instead of delegating those responsibilities to smaller units.

Spongebob — God Class version!

A good guiding principle for finding such a class is, try writing down a brief description of the class without using conjunctions like if, and, also, etc.
If you can’t, then probably the class is handling multiple responsibilities.

Let’s apply the principle here, 
*‘The Dosa Maker class takes orders and payment, prepares the dosa and serves it to the customer.’*

The Wise Developer:Do you know why classes with overloaded responsibilities are a bad idea?
Mohan: Hmm, I can think of a few reasons

  • Testing such classes is hard, because 
    A: God classes usually have complex state variables and too many permutations of states that need to be tested.
    B: for each test, we need to write significant logic just for state initialization and management.
  • A class with too many reasons to change has too many reasons to break as well. And it’s hard to figure out what went wrong.

The wise developer: Correct!

But there are a couple of concepts that you need to be aware of, to completely understand why overloading is a bad idea, and those are :

“Overloading an entity with multiple responsibilities Increases Coupling and Decreases Cohesion.” 

Mohan: What does that mean? 🤔

A good system should be ‘high’ on cohesion and ‘low’ on coupling.

1. Cohesion

Cohesion refers to the degree to which the elements inside a component belong together. 

In other words, Cohesion measures the relevance between the logical sub-units within a component. For example, Can you comment on the cohesiveness of this class? 
💡Hint: Try finding the logical sub-units first.

The Wise Developer: What Do you think? I this class ‘highly cohesive’ or ‘slightly cohesive’?
Mohan: *If we look at the dependency graph of the above class, we can clearly see 3 disconnected entities.* 

Dependency Graph of Breakfast Maker Class

As there is no dependency between these sub-units, I think this class can be categorized as ‘slightly cohesive’.

The Wise Developer: Yes that’s a correct observation. 

If your class or system is ‘slightly cohesive’, it implies that

  • The system can be broken down further into smaller and ‘highly cohesive’ units.
  • The logical flow of the system is ‘trespassing’ your system boundaries, which means ‘redefining’ these boundaries can make the system ‘more cohesive’.

Similarly, A ‘highly cohesive’ system implies that 

  • It can be considered as an ‘Atomic’ unit that cannot be easily broken down into smaller and more cohesive units
  • All the logical dependencies required for its function are inside not outside.

For example, A mobile phone can be considered as a highly cohesive system, it has multiple logical sub-units, like a battery, screen, circuit board, camera, etc, and all are dependent on each other, but since all these dependencies are inside our system boundary, it functions very well as an atomic unit.

2. Coupling

Coupling is the degree of interdependence between software modules.

In other words, Coupling measures the relevance between multiple modules, i.e, how closely connected two modules are and what is the strength of the relationships between modules. Low Coupling means that there are fewer dependencies on other modules, thereby making systems easier to change, test, and reuse in the future. Conversely, High Coupling means that your module is ‘strongly’ dependent on other modules for its function, thereby making the system 

  • Brittle, As changes in one sub-system can ‘potentially’ cause failure in another sub-system.
  • Harder to Test, As you will need to mock all those dependencies before writing any tests.
  • Harder to Reuse, As the system is not isolated. 

Food For Thought : Reusability vs Coupling

Life Is a balancing act of opposing forces!

Mohan*: Is it even possible to have a ‘completely independent system? Don’t you think we will end up re-implementing the functionality of other modules if we focus solely on loose coupling?*

The Wise Developer: Any system will have some ‘degree’ of dependence on another system, hence it's not possible to create a completely independent system. And you are right, if we focus solely on loose coupling, we will end up violating the DRY principle, which states that we should strive for reducing duplication in our code. 

*As it happens in life as well, there is no correct answer here, what you need to remember is that ‘a wise developer always strives for balance in conflict’. i.e. Whenever you have to make a decision between duplicating code and reducing coupling, do the cost-benefit analysis of both approaches, try thinking about what would be more beneficial in the longer term of the project life cycle, and make the decision accordingly.*


Summary

To summarise, 

“A class should have only one reason to change” 
— Robert C. Martin

This is popularly known as the Single Responsibility Principle (SRP), a term coined by Robert C. Martin, author of SOLID principles.

As an exercise, try breaking down the above Dosa Maker Module using this principle.

💡 Hint: There are 3 separate responsibilities currently managed by the Dosa Maker class, that is,
1. Managing orders and payments (a cashier ?)
2. Preparing the dish (a chef ?)
3. serving the dish (a waiter?)

Enjoy learning, Enjoy oops!

We welcome your comments

Subscribe Our Newsletter

Get well-designed application and interview centirc content on ds-algorithms, machine learning, system design and oops. Content will be delivered weekly.