This is a series of 5 articles related to solid principles of object-oriented design. Picture credited to Stephen Haunts.
Introduction.
SOLID principles are a great guideline to design object oriented code. Each of them address a desirable feature in all systems and following them will simplify our code and design.
This is a post with examples and theory as well as some recommendations to read. This is the first of five articles
Single Responsibility Principle.
Background:
This is the first SOLID principle. The term was introduced by Robert C. Martin in his book Agile Software Development, Principles, Patterns, and Practices.
Intent:
The intent is to make a class or module to have one, and only one reason to change.
What does it solve?
It allows a better separation of concerns and allows more cohesive classes and modules thus making modifications less critical by just changing what’s exactly needed at the moment. It also allows small classes that are easy to use, understand and modify.
Common Issues:
This is a principle that sounds great in theory but takes a lot to do it right in practice. You might end up with lots of too lazy classes with very little responsibility that might evolve to the worst kind of objects “the data buckets with just getters and setters” and lots of services consuming them.
Example:
Assume you have a system which requires CRUD(Create Read Update Delete) operations for users. Each user have information such as email, full name, birthday, etc.
If we let our user class to handle the persistence such as some ORM(Object Relational Mapping) technologies do, we would end up with a class that have more than one reason to change, when the user updates or add information and when the user is persisted or searched in the system.
UML Diagram for a poorly designed user class.
This is clearly a bad idea, if we separate the persistence layer to a new class the User class would remain as a class for user information and will only have one reason to change: if the user information is modified or used in a different way and the persistence options could become more flexible.
Uml diagram for a better designed user class and its repository.
Now there’s a clear separation of concerns, the User class is ignorant of the persistence logic and the User repository could make use of different persistence strategies without effort. However, the downside is the User class now is just a data bucket with getters and setters, but chances are the User class will evolve eventually in a more complex class. Another downside with this movement is that there’s a dependency between the User Repository and the User class making the system more complex in implementation but simpler in design and separation of concerns.
The general rule here is to acknowledge the different alternatives and choose the one that best fit your needs. Principles are guidelines not rules but the major advantage of following the principles are a set of common solutions that are agnostic of languages, easy to use and understand for both novices and expert developers and simplicity in our design.
Additional Resources
Some valuable techniques that we can learn to separate classes in an existing system are the ones described in Martin Fowler’s book Refactoring: Improving the Design of Existing Code such as:
And their counterpart if we find that we separated too much our classes:
Further reading:
I recommend the following links if you want to know more about the subject:
- Curly’s Law: Do One Thing at CodingHorror.
- Single responsibility principle at Wikipedia.
- Single Responsibility Principle at OOODesign.
Please feel free to ask me more questions or leave suggestions. Hope this was useful and Happy Coding!
