SOLID: Single Responsibility Principle
This is the first in the series of posts on SOLID Software Principles. The first of the SOLID Principles – The S in SOLID, is the Single Responsibility Principle.
It’s purpose is simple and concise to state – Every class should have only a single responsibility.
What does that mean? And why should you care about it?
More often than not, you find classes that do a lot of things. Completely functional and working code, but the kind that do not reveal what their purpose is. In all likelihood, the only way to understand the code flow is probably to debug the code.
The uglier varieties of these classes are the ones that no developer understands and wants to modify. In case of a bug, the fix is a partial guess-work and you are not quite sure if it is going to break other parts of the system.
For the sake of simplicity, I provide an easy to understand example that violates the Single Responsibility Principle.
/**
* An example of a Class that violates the Single Responsibility Principle.
* Creation of customer and order mixed up.
*
* @author deepak.karanth
*/
public class CustomerOrder {
public void createCustomer(int customerId, String name) {
// Create customer
}
public void submitOrder(Cart shopCart) {
// Step 1: Submit Orders for processing
Order order = orderProcessor.submitOrder(shopCart.getItems());
// Step 2: Update customer object with the newly processed order information
Customer customer = shopCart.getCustomer();
customer.addOrder(order);
dbManager.update(customer);
}
}
Now, lets us understand what the principle is about and how to address the issues in the above example.
Single Responsibility Principle
As stated before, the intent of this principle is to make sure that classes have a single responsibility.
What does responsibility mean? It is ambiguous and open for interpretation. Hence, an alternate version of the same principle is
A class should have only one reason to change.
When said in the above manner, the meaning is a lot clearer. However, there are still some open questions – How far do we take this rule? What code artifacts should follow this principle?
1. Putting the principle into practice
When looking to apply this principle in practice, I look at it from 3 levels. Each of the 3 levels should adhere to the principle in different manners.
Package level
At the package level, make sure that all the classes within it belong as one logical unit. It could mean grouping classes in to packages based on their business domains, features they implement or any other logical separation.
For example, all classes related to Customer domain and in a package. Within this package, there may be separate sub-packages for entities, business rules, web services etc.
When packages are organized in such a manner, it is easy to identify classes in the code.
Class level
At the class level, the principle should be applied to make sure that methods that are of similar nature are grouped in the same class. Only cohesive methods should exist together within a class.
Going back to the example above, the class CustomerOrder has two methods, both of which handle completely different aspects – one creates a customer and the other manages the order processing. If there needs to be change in the logic for creating a customer, it should never cause failures in the order creation process. In order to make sure such errors do not occur, the two methods have to be separated into different classes.
Method level
At the method level, it has to be made sure that the method is only doing one thing, and one thing only. A method that has many paths of execution, has many invocations to other methods is generally an indication that the method is having too many purposes.
In the example, the submitOrder method has two responsibilities – to submit the order for processing and to update the customer object with the newly created order information.
These are two distinct responsibilities and should be separated into different methods(and moved to different classes if necessary)
2. Benefits
There are a number of ensured benefits of using the Single Responsibility Principle – both to the code and the programmer.
- Easy to extend the class with functionality because unrelated code isn’t present in the same class.
- Dependency management aspects between classes become easier to manage since the code is grouped well.
- Code changes are isolated and the unknown impacts on other areas of the system are greatly reduced.
- Classes will be a lot smaller and hence it will is easy to understand the code. A good set of naming conventions for the classes and the methods in addition to adhering to the principle further increase the benefits.
- Easy to identify defects and fix problems in the code.
- Easier to write optimal tests since the functionality provided by the class is much clearer.
- On-boarding a new team member is easier since the code is well-organized and easy to understand.
3. Caution
It is easy to go over board in trying to adhere to this principle. The effort of refactoring the code to adhere to the principle vs. the long-term benefits must always be evaluated. On a lighter note, if you take it to the extreme and could end up with only a single method per class!