Design Principles - Single Responsibility Principle

       Definition: There should not be more than one reason for a class change. In layman's terms, a class is only responsible for one responsibility.

 

       Origin of the problem: Class T is responsible for two different responsibilities: Responsibility P1, Responsibility P2. When the class T needs to be modified due to changes in the requirements of the responsibility P1, it may cause the function of the responsibility P2 that is running normally to malfunction.

       Solution: Follow the Single Responsibility Principle. Two classes T1 and T2 are established respectively, so that T1 completes the function of duty P1, and T2 completes the function of duty P2. In this way, when the class T1 is modified, the responsibility P2 is not at risk of failure; similarly, when the class T2 is modified, the responsibility P1 is not at risk of failure.

         When it comes to the Single Responsibility Principle, many people dismiss it. Because it's too simple. Even experienced programmers who have never read Design Patterns and have never heard of the Single Responsibility Principle will consciously follow this important principle when designing software because it is common sense. In software programming, no one wants to modify one function to cause other functions to fail. The way to avoid this problem is to follow the Single Responsibility Principle. Although the Single Responsibility Principle is so simple and considered common sense, even the programs written by experienced programmers will have code that violates this principle. Why would such phenomenon happen? Because of the spread of responsibility. The so-called responsibility diffusion is that for some reason, the responsibility P is divided into finer-grained responsibilities P1 and P2.

         For example: class T is only responsible for one responsibility P, so the design is in line with the principle of single responsibility. Later, for some reason, maybe the requirements changed, or maybe the designer of the program has improved, and the responsibility P needs to be subdivided into finer-grained responsibilities P1 and P2. At this time, if the program is to follow the single responsibility principle, it is necessary to The class T is also decomposed into two classes T1 and T2, which are responsible for the two responsibilities of P1 and P2 respectively. But when the program is already written, doing so is simply too time-consuming. So, simply modifying class T to use it for two responsibilities is a good choice, although doing so would be contrary to the single responsibility principle. (The risk of doing this is the uncertainty of duty proliferation, because we don't think that duty P, in the future, may spread to P1, P2, P3, P4...Pn. So remember, when duties spread to a point beyond our control Refactor the code immediately before it reaches the required level.)

 

For example, use a class to describe the scene of an animal breathing:

    class Animal{  
        public void breathe(String animal){  
            System.out.println(animal+"breath air");  
        }  
    }  
    public class Client{  
        public static void main(String[] args){  
            Animal animal = new Animal();  
            animal.breathe("牛");  
            animal.breathe("羊");  
            animal.breathe("猪");  
        }  
    }  

 

operation result:

Cow breathe air
Sheep breathe air
Pig breathe air

 

        After the program was launched, a problem was found. Not all animals breathe air. For example, fish breathe water. If you follow the single responsibility principle when modifying, you need to subdivide the Animal class into Terrestrial for terrestrial animals and Aquatic for aquatic animals. The code is as follows:

    class Terrestrial{  
        public void breathe(String animal){  
            System.out.println(animal+"breath air");  
        }  
    }  
    class Aquatic{  
        public void breathe(String animal){  
            System.out.println(animal+"breathing water");  
        }  
    }  
      
    public class Client{  
        public static void main(String[] args){  
            Terrestrial terrestrial = new Terrestrial();  
            terrestrial.breathe("牛");  
            terrestrial.breathe("羊");  
            terrestrial.breathe("猪");  
              
            Aquatic aquatic = new Aquatic();  
            aquatic.breathe("鱼");  
        }  
    }  

 

operation result:

Cattle breathe air
Sheep breathe air
Pigs breathe air
Fish breathe water

 

We will find that if this modification costs a lot, in addition to decomposing the original class, we also need to modify the client. Although directly modifying the class Animal to achieve the purpose violates the principle of single responsibility, the cost is much smaller. The code is as follows:

    class Animal{  
        public void breathe(String animal){  
            if("鱼".equals(animal)){  
                System.out.println(animal+"breathing water");  
            }else{  
                System.out.println(animal+"breath air");  
            }  
        }  
    }  
      
    public class Client{  
        public static void main(String[] args){  
            Animal animal = new Animal();  
            animal.breathe("牛");  
            animal.breathe("羊");  
            animal.breathe("猪");  
            animal.breathe("鱼");  
        }  
    }  

 

       As you can see, this modification is much simpler. But there are hidden dangers: one day it is necessary to divide the fish into freshwater-breathing fish and seawater-breathing fish, and then it is necessary to modify the breathe method of the Animal class, and the modification of the original code will call "pig", "cow"" Sheep" and other related functions bring risks, maybe one day you will find that the result of the program running becomes "cow breathing water". This modification method directly violates the single responsibility principle at the code level. Although it is the easiest to modify, the hidden danger is the greatest. There is another modification:

    class Animal{  
        public void breathe(String animal){  
            System.out.println(animal+"breath air");  
        }  
      
        public void breathe2(String animal){  
            System.out.println(animal+"breathing water");  
        }  
    }  
      
    public class Client{  
        public static void main(String[] args){  
            Animal animal = new Animal();  
            animal.breathe("牛");  
            animal.breathe("羊");  
            animal.breathe("猪");  
            animal.breathe2("鱼");  
        }  
    }  

 

       It can be seen that this modification method does not change the original method, but adds a new method to the class. Although this also violates the single responsibility principle, it is in line with the single responsibility principle at the method level, because it does not There is no code to move the original method. These three methods have their own advantages and disadvantages, so which one should be used in actual programming? In fact, this is really difficult to say, it needs to be determined according to the actual situation. My principle is: only the logic is simple enough, the single responsibility principle can be violated at the code level; only the number of methods in the class is small enough, the single responsibility principle can be violated at the method level;

        For example, the example in this article is too simple. It has only one method, so whether it is a violation of the single responsibility principle at the code level or at the method level, it will not cause much impact. Classes in practical applications are much more complex. Once the responsibility is diffused and the class needs to be modified, unless the class itself is very simple, it is better to follow the single responsibility principle.

 

The advantages of following the Single Responsibility Principle are:

  • It can reduce the complexity of the class. A class is only responsible for one responsibility, and its logic must be much simpler than responsible for multiple responsibilities;
  • Improve the readability of the class and improve the maintainability of the system;
  • The risk caused by changes is reduced. Changes are inevitable. If the single responsibility principle is followed well, when one function is modified, the impact on other functions can be significantly reduced.

        It should be noted that the single responsibility principle is not only unique to the idea of ​​object-oriented programming, as long as it is a modular program design, the single responsibility principle applies.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326438058&siteId=291194637