Let's dive into one of the most important aspect of Object-Oriented Design : the single responsibility principle. The shortcut for this idea is :
Things that change for the same reason should be grouped together and things that change for different reasons should be separated.
I learned this principle in Sandi Metz's excellent book POODR and this article is mainly extracted from my notes on the book.
Organizing code to allow for easy changes
When thinking about code, we have to understand that it should always be T.R.U.E :
- Transparent : consequences of changes in code should be obvious.
- Reasonable : cost of change should be proportional to the benefits the change achieves.
- Usable : code parts should be usable in new and unexepect contexts.
- Exemplary : code should encourage new developers* to perpetuate those qualities.
*new developers can also mean you 6 months from now.
Why single responsibility matters
Because applications that are easy to change consist of classes that are easy to reuse, and a class that has more than one responsibility is difficult to reuse. You increase your application's chance of breaking if you depend on classes that do too much.
Determining if a class has a single responsibility
A clear way to determine if a class has a single responsibility is to describe the class in one sentence. If the simplest sentence you can come with uses the word and, then it likely has more than one responsibility. And if it uses the word or, it has more than one responsibility and they aren't even related.
Depend on behavior, not data
I think the motto Don't Repeat Yourself is a shortcut for this idea. The foundation of an OO system is the message, but the most visible element is the class, that's why, in my opinion, we always think about classes and methods first instead of thinking about the behavior (i.e what the object really wants).
In Ruby, we can access data in one of two ways : refer directly to the instance variable or wrap the instance variable in an accessor method. Let's say we have a
Shop with a single
item and a number of
Here is an implementation of
Shop referring directly to the instance variable :
A better way of doing this is by always hiding the variables, even from the class that defines them. We do this by wrapping them in methods. Send messages to access variables, even if we think as them as data. Here is the
Shop example with instance variables hidden :
Methods should also have only one responsibility
We can also apply everything we said about classes to methods. So we can also ask them what they do and try to describe their responsibility in one sentence. Let's try to implement a
total_revenues method that, given a list of
shops, returns the total revenue of all shops.
Let's see an example on how to separate methods into smaller methods with narrowed responsibility. Begin with this method :
Separating into smaller methods :
Even better, delegating to the shop object the behavior on how to get its total revenue :
Separating iteration from the action that is being performed is a common case of multiple responsibility.
A last thing I would like to share is about commenting code. I usually try to avoid comments : instead I create a new method to isolate the behavior and the new method name serves the same purpose as the old comment. It forces you to be really explicit about the code you write.
Always remember that classes that have a single responsibility isolate that one thing from the rest of the application. This isolation allows change without consequence and reuse without duplication.
In other words : it increases developers happiness !
Always keep learning