Introduction
SOLID Design Principles are tips and tricks to organize the code in different classes which helps in keeping the code clean, maintainable and scalable (or extensible).
The way software modules & classes are designed & organized have a major impact on the people associated with it:
- Programmers: The application can be easy to manage and extends.
- Business owners: The application can be quickly enhanced to add business critical and compliance mandatory functionalities.
- Users: The application can provide improved experience and help achieve more.
The Single Responsibility Principle “S”
As the name suggests, a class should be responsible for one and only one thing. In words of Robert C. Martin “A class should have only one reason to change”.
Although it seems pretty obvious & it is basic Object Oriented Design, but still it gets defied many times.
Consider this example of LogManager class having a field Enum LogType with possible values of FileBased, DBBased:

The log() method converts Object to string and logs the string to File or DB conditionally:
void log(Object obj) {
String message = convertToJson(obj);
switch(logType) {
case FileBased:
logToFile(message);
break;
case DBBased:
logToDB(message);
break;
}
}
The purpose of above code is to perform logging, it seems to be doing fine by logging into a File or DB and it includes some helpers like converting an Object to JSON string.
However, this class has two reasons to change:
- Changes in the way messages are logged
- Changes in the way String is created
In future, it may be decided to log messages as xml or csv or any other format. OR a different library may be used for converting Object to json string. But it should not impact the way strings are being persisted to storage (either File or DB).
Doing multiple things in a single class makes it tightly-coupled:
- Some variable may be reused
- Low level and Mid level methods may get mixed. e.g. log(String msg) should have been a low level method (only writing the string to storage) but LogManager class has log(Object obj) which is mid level method invoking convertToJson(Object).
In a class like this, it becomes very difficult to change something in isolation. A fix in one part of class impacts other parts too and might break something unintentionally.
A better design is to create a Serializer Interface for converting Object to string. Implement a JSONSerializer for json string conversion.
Use abstraction (Serializer) in the LogManager class:

The LogManager class doesn’t need to know how serialization is happening and the log() method should depend on Abstraction for Serialization:
void log(Object obj) {
String message = serializer.serialize(obj);
switch(logType) {
case FileBased:
logToFile(message);
break;
case DBBased:
logToDB(message);
break;
}
}
Conclusion
Single Responsibility principle helps in :
- Making the classes loosely-coupled
- Minimizing the impact of a change (bug-fix or enhancement)
- Making the code easy to understand