🧑‍💻

#16 SOLID - Single Responsibility principle

2024/07/31に公開

Introduction

While we study programming or as a software engineering students, the first thing we going to learn is SOLID principles.
(Well, technically how to code is the first, lol)
It is very basic common sense to follow these five principles to make your programmer's life easier, but after handling different projects or learning different programming languages for long years, we may still follow them but forgot what they are. So, let us have a small revision.

What is SOLID?

SOLID principles are first introduced by famous American software engineer Robert Cecil Martin in his 2000 paper. SOLID stands for five design principles of object-oriented programming which are

  • Single Responsibility principle
  • Open-closed principle
  • Liskov Substitution principle
  • Interface Segregation principle
  • Dependency Inversion principle

These five design principles aim to let developers can easily work on by creating understandable, readable and testable code.

Single Responsibility principle

A module should be responsible to one, and only one, actor.

To explain, let's have some example codes.
We want to calculate the sum of the length long of all the shapes in a list, and there are circle and square shapes for example.

    class Square {
            public int length;
            public Square(int length) {
                    this.length = length;
            }
    }
    
    class Circle {
            public int radius;
            public Circle(int radius) {
                    this.radius = radius;
            }
    }

And here we may have a length calculator to sum up the length of the shapes and display the value.

    class LengthCalculator {
            private Square[] squares;
            private Circle[] circles;
            
            public LengthCalculator(Square[] squares, Circle[] circles) {
                    this.squares = squares;
                    this.circles = circles;
            }
            
            public int sum() {
                    int length = 0;
                    for(int s : squares) {
                            length += 4*s.length;
                    }
                    for(int c : circles) {
                            length += 2*Math.PI*c.radius;
                    }
                    system.out.println(length);
            }
    }

And now we want to create some shapes and to display it.

    Square[] squares = new Square[] {...};
    Circle[] circles = new Circle[] {...};
    
    LengthCalculator calculator = new LengthCalculator(squares, circles);
    
    calculator.sum();

Now all the parts are ready and you may think there is nothing wrong with it. YES, at least no syntax error. But it is not OOP at all and if you start to maintain this program, you will feel pain.

Let's focus on the Single Responsibility principle first.
The LengthCalculator both handle sum and display logic. What if we want to change the format to display? Change the word? Every change we have, every time we need to edit this class. Still OK? What about we want both English output and Japanese output?
We can observe the current code is hard to handle different kind of scenario because all the logic are handled by the LengthCalculator class which violate the principle. As a sum calculator, it should focus on how to sum up the value and not care about other logic.

To resolve this issue, we can create another class OutputPrinter to handle display logic.

    class OutputPrinter {
            private LengthCalculator calculator;
            public OutputPrinter(LengthCalculator calculator) {
                    this.calculator = calculator;
            }
            public void print() {
                    System.out.println(calculator.sum());
            }
    }

And the display class would work as below

    Square[] squares = new Square[] {...};
    Circle[] circles = new Circle[] {...};
        
    LengthCalculator calculator = new LengthCalculator(squares, circles);
    OutputPrinter printer = new OutputPrinter(calculator);
    printer.print();

Now it satisfies the Single Responsibility principle and it looks.....less worse?

Here remain four principles did not mentioned yet.

  • Open-closed principle
  • Liskov Substitution principle
  • Interface Segregation principle
  • Dependency Inversion principle

We will keep following up the code and the principles. See you in the next post.

References

Discussion