SOLID Programming (1) – Single

SOLID is an acronym for several of the most useful OOP design principles:

In this post, I will cover the first principle, Single Responsibility. I hope these posts will give you an understanding of how these principles can improve the way you write code and maybe impress the next person that reviews your code!

Single Responsibility Principle

“A class should have only one reason to change”

– Robert C. Martin

A class should encapsulate responsibility. A simple way to implement this is to group together code that is going to change for the same reason. This allows us to write modular code that is easily testable and maintainable.

Another reason for learning this principle is to learn how and when to refactor code. Take a look at your source control and find the top five most touched files. There is a good chance these classes are breaking the Single Responsibility Principle (SRP). If these areas are constantly changing or, worse yet, frequently creating bugs this may be a great place to apply this principle.

git log --pretty=format: --name-only | sort | uniq -c | sort -rg | head -10

Single

What does “single” mean? Stop right there. This does not mean you should be writing one method classes! This simply means all the code in a module should generally have the same theme or reason to change. Let’s review an example.

Accounting software may have many different calculations based on inputs from different forms. At a high level it may make sense to keep all of these calculations encapsulated into one class, but this can become quite cumbersome. It may make more sense to break down each calculation into a class for a given form; you can have one class responsible for calculating a W-2, while another handles a W-4 form. This way the only reason a class would have to change is if it’s corresponding form changes. Separating these actions into their own class can reduce complexity, increase ease of use and result in a SOLID design.

A “God Class” is an object that controls too many other objects in a system and has grown beyond all logic to become, The Class That Does Everything.

A common symptom of “single” is having common dependencies (@Autowired or Parameterized). If you have class B and class A requires class B to do work these classes are considered coupled. Now say class A needs classes B-Z to do all its work, this is now considered a problem. If a class has an abundance of dependencies it can’t possibly be doing a single action! This is often a red flag, so keep an eye out!

Pro Tip: When refactoring a God Class start with abstracting dependencies to a new class and take the dependent code with it!

Bad Example

I would like to demonstrate this principle by starting with a bad example, God classes. A God class is an object or class that tries to do everything. These modules have a certain code smell to them. In my experience, a large number of dependencies or classes that are several thousand lines long usually give off this type of aroma. There are many static code analysis tools out there that help identify God classes.

Let’s create an example of a God class. Someone has written a class responsible for a single entity. However, there is complicated logic for creating, updating and deleting these types of objects. Each action has its own set of validations and permissions. There will be many new sub-types of this entity with their own set of rules down the road. A programmer may think this is a great idea and this design may work well for a long time without causing any issues. All the code is in one place, a simple Ctrl+F will find almost anything, whats the problem? But this can potentially get very messy! Allow me paint you a portrait.

The implementation may start off smooth but your code quickly starts to become more and more coupled with every new wave of business logic. Adding new features starts to become very cumbersome, bug fixes produce signification implications across the entire class. After several thousand lines of code you start to feel like you’re drowning, you know longer know up from down, or which way is left or right. Everything is fragile, held together with duct tape code and paper clips comments. You stay awake at night thinking about whats going to happen if you remove that “one line of code”.

An unlucky developer will most likely get to spend part of their career maintaining this code base. She may need to refactor a single function one day and this will certainly set off a chain reaction, causing thousands of lines of code to be refactored (hopefully someone wrote tests!). Sadly, she is a casualty in this situation, weighted down by poor design decisions, caused by a developer who knew no better way.

Has any of this put a smile on your face? Were you ever “that” developer? Thats great news! This means you have had the opportunity to see what “sloppy” code looks like and if you continue to read on you may be able to reduce these types of “pain in the code”. Better yet, maybe you can prevent them from happening!

This is a clear example of a class having too many responsibilities and should be avoided. These different layers can quickly become overly coupled and cause adverse side effects. Now let’s see how we can fix it.

Good Example

Moving on from this daunting example, is there any hope for our unfortunate developer? There is a light at the end of the tunnel and maybe a few extra bucks in her bonus this year! Our motivated employee pitched an idea to management that would save them money and improve their morale with a given client. She explained this “God Class” is an area of code that frequently changes and produces many pesky bugs. Developers have spent significant time over the span of several sprints to fix issues within this area. The client keeps coming up with new features they want added to this corresponding component and have annual changes that need to be implemented. Spending time on a refactor would not only make the codebase more manageable and maintainable, but also reduce the amount of issues raised by the client, a win win for both developers and management! She was very persuasive and her manager agreed to give her a bucket of hours to accomplish this task. Her manager understands that sometimes an upfront cost can save you time and money in the long run. Time to get this party started!

How do we get started? Lets discuss a few ways to move forward with implementing the SRP. Pretend you are building a mini API, think about “How would I like to use this class?” as opposed to “What should this class do?” At a high level, we want this class to encapsulate everything to do with SomethingClass, however that is what got us into this situation. You need to think about relationships, ponder what SomethingClass and SomethingElseClass have in common. Can these relationships can be abstracted? Another aspect to think about is functionality. Does there exist complex processes that might warrant a class of their own? Common examples could be CRUD operations, validations, aggregation, or decoration.

Start with a good strategy, separating logic into coarse-grained classes and fine-grained classes. Ensure these coarse-grained classes still follow SRP! Then we are left with two options. If the fine-grained classes are not used anywhere else, move their implementation back into the coarse-grained classes. Else, you can leave the fine-grained classes if you are happy with the way they’re designed. A good test to see if you are following the SRP is asking yourself, for what reasons should this class change? Hint: there should only be one reason!

“Gather together the things that change for the same reasons. Separate those things that change for different reasons.”

https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html

Conclusion

Once you are done refactoring make sure you take time to reflect and ensure these changes are an improvement to maintainability. You should design your classes to be easily manageable and encourage ease of use. While God classes also have their place in software development, they typically do not have either trait.

Writing code is an art form. It’s very similar to how a musician writes music or how an artist paints a portrait. Becoming a great artist takes time and practice. It takes making mistakes, learning what doesn’t work, and how to go about solving problems. Learning from blunders and being able to recognize when you’re faced a problem you have already solved or having the ability to model similar problems. Sometimes you need to think outside the box, while other-times keeping things simple is often the best solution.

If you liked this post feel free to follow me! I would like to take you on a journey of becoming the best you can possible be.

Resources:

https://stackify.com/solid-design-principles/

https://www.toptal.com/software/single-responsibility-principle

https://hackernoon.com/you-dont-understand-the-single-responsibility-principle-abfdd005b137

https://en.wikipedia.org/wiki/Active_record_pattern

https://softwareengineering.stackexchange.com/questions/150760/single-responsibility-principle-how-can-i-avoid-code-fragmentation

https://codeburst.io/understanding-solid-principles-single-responsibility-b7c7ec0bf80

Leave a comment