As those who work with me certainly know by now, I am a strong proponent for the SRP and try to practice it and convince others to do the same whenever I can.
SRP stands for Single Responsibility Principle, and states that a class should have one responsibility and one, and only one, reason to change.
Now, truth be told, when talking about it abstractly, I am rarely told it is a bad idea. After all, one of the first things you learn when you start programming is to "divide and conquer". Breaking down big complex problems into smaller simpler ones is a no-brainer.
But ... have you ever been accused of over-engineering because your class diagrams tend to be larger, because you have lots of classes?
Here are some of the arguments I have heard against using small, simple classes adhering to the SRP principle:
- Too many files will make the system hard to reason and work with.
- Dividing this into smaller classes is a waste of time, because you will never reuse such a simple behavior elsewhere.
- You will loose sight of what code is actually being executed, with so many small pieces involved.
- It is working now, there is no need to change it.
- You will write more code that way.
- Using SRP is not realistic in a real, complex, project
- This is just adding (runtime) overhead, because you will need to instantiate more objects and call more methods to get the job done.
Here are my counter arguments, and some quotes from "uncle" Bob Martin's book Clean Code.
Please feel free to comment, agreeing or disagreeing with me. I do love to change my mind, when I feel I'm learning in the process.
1. Too many files will make the system hard to reason and work with.
I’ll start by quoting Bob Martin:
"a system with many small classes has no more moving parts than a system with a few large classes. (...) Do you want your tools organized into toolboxes with many small drawers each containing well-defined and well-labeled components? Or do you want a few drawers that you just toss everything into?"
I strongly believe that if the class names are carefully chosen, the system tends to get easier to understand. You suddenly start feeling more productive, and it’s because you don’t have to read all of the code all of the time. So yes, SRP will lead to a cleaner design, which in turn will improve code clarity and legibility.
2. Dividing this into smaller classes is a waste of time, because you will never reuse such a simple behavior elsewhere.
This is in fact just the opposite. Consider you have a big, complex class, and several simpler ones. Which ones do you think is more likely to be reused ? Yes, by having smaller classes, you will make code reuse much more likely. It is so much simpler to just use an already tested class, that you know exists in your code base than a block of code that is mixed with other concerns, deep in the middle of another big class, that does a lot more stuff.
3. You will loose sight of what code is actually being executed, with so many small pieces involved.
Well, let me repeat the first quote from Bob Martin. “a system with many small classes has no more moving parts than a system with a few large classes”. If you don’t overgeneralize just for the sake of it, you should end up with roughly the same overall complexity (or maybe less), but with a much cleaner design and implementation.
Remember, choosing a class name is extremely important. Some consider it an art. Doing so will make your life a lot easier when reading the code. And to quote Bob Martin (again):
"If we cannot derive a concise name for a class, then it's likely too large".
Think of all those overly generic names you have surely seen around like “XyzManager” and “XyzHandler”, what do those classes do ?
4. It is working now, there is no need to change it.
Remember that code will start to rot if you don’t keep it clean and keep improving and refactoring it. And if you are discussing this, chances are it is not as clean as it should be. If you refactor it into several smaller classes each with a clear responsibility, you will improve code clarity, testability, reusability, etc.
5. You will write more code that way
You will end up with more methods and classes, but they will all be simple. As for the total size, that should be smaller in fact, because you will be able to reuse code in a lot more places than you used to.
6. Using SRP is not realistic in a real, complex, project
It is not only possible, it has been done. Now, refactoring a complex code base to make it comply with SRP will not be an easy task, but will surely pay of.
7. This is just adding (runtime) overhead, because you will need to instantiate more objects and call more methods to get the job done.
This is a somewhat hard argument to counter, because it is, in my oppinion, the one that has more truth to it. But, like everything, it is debatable. And I normally present these counter-arguments:
- Giving up SRP for this reason is premature optimization. And we all know that is the root of all evil.
- Depending on the language being used, the compiler may optimize the method calls. If there is one use case for compilers to optimize, it method calls to simple methods.
- Sometimes, even with all of this separation, the complex system will waste more resources, because optimizing it is a lot harder due to its sheer complexity.
Let me finish by quoting Bob Martin's Clean Code again. Yes, I love this book. If you haven't read it and are serious about programing and producing good code, your really should read it.
"SRP is one of the more important concepts in OO design. It's also one of the simpler concepts to understand and adhere to. Yet oddly, SRP is often the most abused class design principle. We regularly encounter classes that do far too many things. Why? Getting software to work and making software clean are two very different activities."
In the end, remember SRP is just a principle, not a strict rule. Just don't give up on it too quickly.
Please forgive me for the rant, but I needed to write it because I love my work, and love to write (and read by the way) clean, elegant code. When I need to work my way through a byzantine labyrinth of code filled with special cases (some of them introduced not by necessity, but intentionally by design!) making it that much harder to read and reason about, I get upset, and start thinking of ways to vent my frustation.