Do you REALLY know what SOLID means? (#1: Exclusive Truth about Single Responsibility Principle)

Do one thing and do it well!
Single Responsibility Principle – This principle states that a class should only have one responsibility. Furthermore, it should only have one reason to change. SRP, like most principles out there, can be over-applied. If we create a new class for incrementing integers, then yeah, that may be a single responsibility, but that’s ridiculous! We tend to forget that things like the SOLID principles are there for a purpose. SOLID is a means to an end, not an end in itself. The end is maintainability.

Let’s consider a scenario. We can think of refactoring a class with two methods into two classes of one method each, so as to reduce the number of services injected into its constructor. We can think that this is a simplifying operation, in line with the SRP (single responsibility principle), when all it does is increases the number of classes to manage as well as adding an interface, in the process adding a lot of boilerplate code.

More often than not; due to blindly and wrongly following SRP makes our code base more difficult to understand and debug, destroy encapsulation by the need to make everything public in order for the class fragments to be able to communicate; and using dependency injection sometimes made impractical by having to inject so many microscopic services in order to get any work done.

Intuitively, it seems to be wrong that our unit of organisation and encapsulation should be used to encapsulate a single method, or even one or two very short methods. In that case, why even bother with classes, and why not go back to procedural coding? In some specific cases it might be justified but we need to be sure of why we would do this.

The single responsibility principle is a way to help us make our code easier to understand and easier to change. You know, I always like to think of the Single Responsibility Principle as the “one-trick pony” of coding. Stay with me here – it’s not as crazy as it sounds! Imagine you’re at a circus. You’ve got your acrobats, your clowns, your lion tamers. Each act does its own thing, and they do it well. You wouldn’t expect the lion tamer to start juggling in the middle of their act, right? That’s the Single Responsibility Principle in a nutshell.

I remember when I first grasped this concept. I was working on this massive, Frankenstein’s monster of a class that did everything from data processing to UI updates. It was a nightmare to maintain. Every time I fixed one thing, another would break. It was like playing whack-a-mole with bugs! That’s when it hit me – this class was trying to be the whole circus. Once I split it up into smaller, focused classes, each with its own responsibility, it was like a weight lifted off my shoulders. Debugging became a breeze, and adding new features? Piece of cake!

It says that each part of our code should only have one job to do. That way, if we need to change something, we only have to look in one place and we don’t have to worry about breaking anything else.

For example, let’s say we have a class that is responsible for getting data from a database and also for formatting that data into a nice report. According to the single responsibility principle, we should split this class into two separate classes: one for getting the data and one for formatting the report. That way, if we need to change how we get the data, we don’t have to worry about breaking the report formatting. And if we need to change the way the report looks, we don’t have to worry about breaking the data fetching.

In other words, each part of our code should only have one reason to change. That way, our code is easier to understand and easier to maintain. But here’s the thing about the Single Responsibility Principle – it’s not a one-size-fits-all solution. I’ve seen developers go overboard, creating a new class for every little function. Before you know it, you’ve got more classes than a university! It’s like trying to eat soup with a fork – technically, you’re following the “use utensils” rule, but you’re missing the point entirely.

The trick is to find the sweet spot. Too few responsibilities, and you’ve got a bloated, unmanageable class. Too many, and you’re drowning in a sea of tiny, interconnected classes. It’s all about balance, folks.

For instance, in a simple todo app, you might have a Task class. It’s responsible for representing a task – its title, due date, status, that sort of thing. But should it also be responsible for saving tasks to a database? Probably not. That’s where you’d want a separate TaskRepository class. See? Two classes, two clear responsibilities. Nice and clean.

Single Responsibility Principle
Reason To Change

We need to pay close attention to the term ‘reason to change’. For example a class for an employee which has methods for calculating and reporting pay, or a class which has data manipulation and data persistence operations; these might be different ‘reason to change’. Also; a reason to change is related to a function in the business which is served by the software: in these cases the classes should be split up because the business function of creating reports and the business function of defining how salaries are calculated are different.

On the face of it, SRP is about where a class has functionality that belongs in two different layers or modules, it should be split. But when people who are following the SRP blindly without understanding the philosophy behind it, they are doing something more general and using it to cut up any class that does ‘more than one thing’.

The idea behind the Single Responsibility Principle is that by limiting the scope of a class or module to a single responsibility, you can make your code more maintainable and easier to understand. When a class or module has only one responsibility, it’s easier to see how it fits into the overall design of your system, and it’s easier to modify or extend the class or module without affecting other parts of the system.

Another apt and proper wording for the SRP is: Gather together the things that change for the same reasons. Separate those things that change for different reasons. It simply means The SRP is about limiting the impact of change.

solid principle
S (single responsibility principle) OLID
Implementing SRP

The trick of implementing SRP in our software is knowing the responsibility of each class. However, every developer has their vision of the class purpose, which makes things tricky. Sometimes only we, as designers of our application, can decide if something is in the scope of a class or not. When writing a class according to the SRP principle, we have to think about the problem domain, business needs, and application architecture. It is very subjective, which makes implementing this principle harder then it seems.

Let’s consider a class that contains code that changes the text in some way. 

public class TextManipulator {
    private String text;

    public TextManipulator(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void appendText(String newText) {
        text = text.concat(newText);
    }
    
    public String findWordAndReplace(String word, String replacementWord) {
        if (text.contains(word)) {
            text = text.replace(word, replacementWord);
        }
        return text;
    }
    
    public String findWordAndDelete(String word) {
        if (text.contains(word)) {
            text = text.replace(word, "");
        }
        return text;
    }

    public void printText() {
        System.out.println(textManipulator.getText());
    }
}

Here we have two responsibilities: manipulating and printing the text. Having a method that prints out text in this class violate the Single Responsibility Principle. For this purpose, we should create another class, which will only handle printing text.

Now, let’s take a closer look at this TextManipulator class. At first glance, it might seem like it’s following the Single Responsibility Principle. After all, it’s all about manipulating text, right? But hold your horses – we’ve got a sneaky little method in there that’s breaking the rules.

See that printText() method? It’s the odd one out. While all the other methods are busy manipulating text, this one’s off doing its own thing, printing text to the console. It’s like having a chef who not only cooks the food but also serves it to the customers. Sure, it’s all related to food, but it’s crossing boundaries.

This is where the “reason to change” part of the Single Responsibility Principle comes into play. If we needed to change how we output text – maybe we want to write to a file instead of printing to console – we’d have to modify this class. But this class should only change if we’re changing how we manipulate text. See the problem?

That’s why we’re going to spin off a separate TextPrinter class. It’s not just about splitting things up for the sake of it – it’s about making our code more resilient to change. And let me tell you, future you will thank present you for this foresight!

Delegating Responsibility
public class TextPrinter {
    TextManipulator textManipulator;

    public TextPrinter(TextManipulator textManipulator) {
        this.textManipulator = textManipulator;
    }

    public void printText() {
        System.out.println(textManipulator.getText());
    }

    public void printOutEachWordOfText() {
        System.out.println(Arrays.toString(textManipulator.getText().split(" ")));
    }

    public void printRangeOfCharacters(int startingIndex, int endIndex) {
        System.out.println(textManipulator.getText().substring(startingIndex, endIndex));
    }
}

Now, in this class, we can create methods for as many variations of printing text as we want, because that’s its job. When designing software based on the SRP principle, cohesion is essential, since it helps us to find single responsibilities for our classes. This concept also helps us find classes that have more than one responsibility.
Let’s go back to our TextManipulator class methods:

...

public void appendText(String newText) {
    text = text.concat(newText);
}

public String findWordAndReplace(String word, String replacementWord) {
    if (text.contains(word)) {
        text = text.replace(word, replacementWord);
    }
    return text;
}

public String findWordAndDelete(String word) {
    if (text.contains(word)) {
        text = text.replace(word, "");
    }
    return text;
}

...

Here, we have a clear representation of what this class does: Text manipulation. But, if we don’t think about cohesion and we don’t have a clear definition of what this class’s responsibility is, we could say that writing and updating the text are two different and separate jobs. Lead by this thought, we can conclude than these should be two separate classes: WriteText and UpdateText. In reality, we’d get two classes that are tightly coupled and loosely cohesive, which should almost always be used together. These three methods may perform different operations, but they essentially serve one single purpose: Text manipulation.

Now, before you go off and start splitting your classes like you’re playing whack-a-mole, let’s have a heart-to-heart about the Single Responsibility Principle. It’s a powerful tool, no doubt about it. But like any tool, it can be misused.

I’ve seen codebases where developers went SRP-crazy. They had more classes than a school district, each responsible for one teeny-tiny thing. Sure, each class had a single responsibility, but trying to understand how they all fit together was like solving a 1000-piece jigsaw puzzle in the dark.

On the flip side, I’ve also seen classes that were more bloated than a Thanksgiving turkey, trying to do everything under the sun. Changing one little thing was like playing Jenga – you never knew what might come tumbling down.

The key, as with most things in life, is balance. The Single Responsibility Principle isn’t about creating the smallest possible classes. It’s about creating classes that are focused, understandable, and easy to change. It’s about organizing your code in a way that makes sense for your specific project and team.

Remember, at the end of the day, the Single Responsibility Principle is a guideline, not a law. Use your judgment. If splitting a class would make your code more confusing or harder to maintain, then don’t do it. If keeping responsibilities together makes more sense in your context, then keep them together.

The goal is to write code that’s easy to understand, easy to maintain, and easy to change. If you’re achieving that, then you’re on the right track, whether you’re following SRP to the letter or not. After all, the best code is the code that gets the job done and doesn’t give your fellow developers (or future you) a headache. So go forth and code responsibly!

Conclusion

The hard thing to remember about the Single Responsibility Principle is that it is based on likely patterns of change, not dependency or functional relationships within the code. Neither the internal structure nor the external function and requirements of the code are key. Rather the nature of the business environment in which the code is undergoing change.

The Single Responsibility Principle (SRP) is a software design principle that states that a class should only have one responsibility, or one reason to change. This can help to make code more maintainable and easier to understand. However, it is important to consider the overall design of the system and the business needs when implementing SRP, as blindly following the principle can sometimes lead to unnecessary complexity and boilerplate code.

It is also important to pay attention to the “reason to change” and to separate responsibilities that change for different reasons. The SRP can be implemented by thinking about the problem domain, business needs, and application architecture, and by considering the cohesion of a class and its clear definition of responsibility. It is subjective and requires careful consideration to effectively implement.

The key is not to overthink!

Learn more on – https://asifulhaque.com/design-patterns-understanding-the-gang-of-four/

Leave a Comment