One of the six principles of "Design Pattern": Summary of Single Responsibility


"Design Pattern" Six Principles Series Links: "Design Pattern" One of the Six Principles: Summary of Single
Responsibility Summary of Principles Four of the Six Principles of "Design Patterns": Summary of the Principle of Interface Segregation Five of the Six Principles of "Design Patterns": Summary of the Principle of Dependency Inversion




The six principles reflect the underlying logic of many programming: high cohesion, low coupling, interface-oriented programming, abstract-oriented programming, and ultimately achieve readability, reusability, and maintainability.

The six principles of design patterns are:

  • Single Responsibility Principle: Single Responsibility Principle
  • Open Closed Principle: Open and Closed Principle
  • Liskov Substitution Principle: Liskov Substitution Principle
  • Law of Demeter: Law of Demeter (least known principle)
  • Interface Segregation Principle: Interface Segregation Principle
  • Dependence Inversion Principle: The principle
    of dependency inversion combines the initials of these six principles (L is counted as one) is SOLID (solid, stable), which means the benefits of combining these six principles: establishing a stable, flexible , robust design.

This article introduces the first principle in SOLID: the single responsibility principle.

1. Definition of Single Responsibility Principle

The Single Responsibility Principle [Single Responsibility Principle, referred to as SRP], also known as the single function principle.

A class or module should have a single responsibility。

We translate it into Chinese, that is: a class or module is only responsible for completing one responsibility (or function ).

As far as a class is concerned, there should be only one reason for it to change.

The so-called responsibility refers to the reason for the class change, that is, the business requirement. If there is more than one reason for a class to be changed, then the class has more than two responsibilities. The single responsibility principle stipulates that there should be one and only one reason to change the industry class.

The core of the single responsibility principle is decoupling and enhancing cohesion.

One sentence summary: a class only does one thing .

Application practice: Do not design large and comprehensive classes, but design classes with small granularity and single function. From another perspective, if a class contains two or more functions that are irrelevant to the business, then we say that its responsibility is not single enough, and it should be split into multiple classes with more single functions and finer granularity.

2. How to understand the Single Responsibility Principle (SRP)?

A class is only responsible for completing one responsibility or function. Don't design large and comprehensive classes, but design classes with small granularity and single function. The principle of single responsibility is to achieve high code cohesion, low coupling, and improve code reusability, readability, and maintainability.

3. How to judge whether the responsibility of the class is single enough?

Different application scenarios, demand backgrounds at different stages, and different business levels may have different judgment results on whether the responsibility of the same class is single. In fact, some side judgment indicators are more instructive and executable. For example, the following situations may indicate that this type of design does not meet the single responsibility principle:

  • There are too many lines of code, functions or properties in the class;

    Will affect the readability and maintainability of the code, we need to consider splitting the class (that is, refactoring).

    If you are a programming beginner without much project experience, in fact, I can also give you a relatively broad and quantifiable standard that can be used, that is, the number of lines of code in a class should preferably not exceed 200 lines , the number of functions and the number of attributes should not exceed 10.

  • The class depends on too many other classes, or there are too many other classes that depend on the class;

    If it does not conform to the design idea of ​​high cohesion and low coupling, we need to consider splitting the class.

  • Too many private methods;

    We have to consider whether we can separate the private method into a new class and set it as a public method for use by more classes, thereby improving the reusability of the code.

  • It is more difficult to give a suitable name to the class;

    It is difficult to summarize with a business term, or can only be named with some general words such as Manager and Context, which shows that the definition of class responsibilities may not be clear enough;

  • A large number of methods in the class focus on certain attributes in the class.

4. Is the responsibility of the class as simple as possible?

The Single Responsibility Principle improves class cohesion by avoiding designing large and comprehensive classes and coupling unrelated functions together. At the same time, the responsibility of the class is single, and the number of other classes that the class depends on and depends on will be reduced, reducing the coupling of the code, so as to achieve high cohesion and low coupling of the code. However, if the split is too fine, it will actually be counterproductive, which will reduce the cohesion and affect the maintainability of the code.

5. Application embodiment

  • The application of the idea of ​​architectural evolution in Android is reflected: too bloated Activity in Android will make you feel very big, and MVP, MVVM and other frameworks are all to make Activity become a single responsibility.
  • The structure is layered, and each layer must have clear responsibilities
  • microservice
  • library
  • module
  • letter of agreement
  • interface design
  • ……

6. Application example 1

For example, we can log in, register and log out on the home page of the website as an example:

public interface UserOperate {
    
    

    void login(UserInfo userInfo);

    void register(UserInfo userInfo);

    void logout(UserInfo userInfo);
}


public class UserOperateImpl implements UserOperate{
    
    
    @Override
    public void login(UserInfo userInfo) {
    
    
        // 用户登录
    }

    @Override
    public void register(UserInfo userInfo) {
    
    
        // 用户注册
    }

    @Override
    public void logout(UserInfo userInfo) {
    
    
        // 用户退出
    }
}

Then if it is split according to the principle of single responsibility, the split is as follows:

public interface Register {
    
    
    void register();
}
public interface Login {
    
    
    void login();
}

public interface Logout {
    
    
    void logout();
}

public class RegisterImpl implements Register{
    
    

    @Override
    public void register() {
    
    
    }
}

public class LoginImpl implements Login{
    
    
    @Override
    public void login() {
    
    
    }
}

public class LogoutImpl implements Logout{
    
    

    @Override
    public void logout() {
    
    

    }
}

In practice, the above can be used as a reference. Whether to split this granularity depends on the specific business. You can also write a rough class at the beginning of the project, and then split and refactor according to business development later, so that you can control it according to the business.

8 Application example 2 (combined with combination mode)

The same responsibilities are put together, and different responsibilities are decomposed into different interfaces and implementations. This is the easiest and most difficult principle to apply. The key is to identify the same type of responsibilities based on business and needs.

Human behavior is divided into two interfaces: life behavior interface, work behavior interface, and two implementation classes.

If an implementation class is used to assume the responsibilities of these two interfaces, the code will be bloated and difficult to maintain. If other behaviors are added later, such as learning the behavior interface, there will be a risk of change (combination mode is used here).

  • define a behavioral interface

    /**
     * 行为包括两种: 生活、工作
     */
    public interface IBehavior {
          
          
        
    }
    

It defines an empty interface, the behavior interface. What are the specific interfaces under this behavior interface? There are two aspects of life and work.

  • Define the living and working interfaces, and they are all subclasses of the behavioral interface

    Life Behavior Interface:

    public interface LivingBehavior extends IBehavior{
          
          
        /** 吃饭 */
        void eat();
    
        /** 跑步 */
        void running();
    
        /** 睡觉 */
        void sleeping();
    }
    

    Work behavior interface:

    public interface WorkingBehavior extends IBehavior{
          
          
        /** 上班 */
        void goToWork();
    
        /** 下班 */
        void goOffWork();
    
        /** 开会 */
        void meeting();
    }
    
  • The implementation class of the above two interfaces

    public class LivingBehaviorImpl implements LivingBehavior{
          
          
        @Override
        public void eat() {
          
          
            System.out.println("吃饭");
        }
    
        @Override
        public void running() {
          
          
            System.out.println("跑步");
        }
    
        @Override
        public void sleeping() {
          
          
            System.out.println("睡觉");
        }
    }
    public class WorkingBehaviorImpl implements WorkingBehavior{
          
          
    
        @Override
        public void goToWork() {
          
          
            System.out.println("上班");
        }
    
        @Override
        public void goOffWork() {
          
          
            System.out.println("下班");
        }
    
        @Override
        public void meeting() {
          
          
            System.out.println("开会");
        }
    }
    
  • Composition of behavior calls

    Next, a set of behaviors will be defined. Different users have different behaviors. Some users only use life behaviors, while some users have both life behaviors and work behaviors.

    We don't know what behaviors a specific user will have, so we usually use a collection to receive user behaviors. What behaviors users have, we add them to it.

    • Behavior composition interface

      public interface BehaviorComposer {
              
              
          void add(IBehavior behavior);
      }
      
    • implement the composite interface

      public class IBehaviorComposerImpl implements BehaviorComposer {
              
              
      
          private List<IBehavior> behaviors = new ArrayList<>();
          @Override
          public void add(IBehavior behavior) {
              
              
              System.out.println("添加行为");
              behaviors.add(behavior);
          }
      
          public void doSomeThing() {
              
              
              behaviors.forEach(b->{
              
              
                  if(b instanceof LivingBehavior) {
              
              
                      LivingBehavior li = (LivingBehavior)b;
                      // 处理生活行为
                  } else if(b instanceof WorkingBehavior) {
              
              
                      WorkingBehavior wb = (WorkingBehavior) b;
                      // 处理工作行为
                  }
              });
          }
      }
      
  • client call

    public static void main(String[] args) {
          
          
            //  张三--全职妈妈 (只有生活行为)
            LivingBehavior zslivingBehavior = new LivingBehaviorImpl();
            BehaviorComposer zsBehaviorComposer = new IBehaviorComposerImpl();
            zsBehaviorComposer.add(zslivingBehavior);
    
            // 李四--职场妈妈 (生活、工作行为都有)
            LivingBehavior lsLivingBehavior = new LivingBehaviorImpl();
            WorkingBehavior lsWorkingBehavior = new WorkingBehaviorImpl();
    
            BehaviorComposer lsBehaviorComposer = new IBehaviorComposerImpl();
            lsBehaviorComposer.add(lsLivingBehavior);
            lsBehaviorComposer.add(lsWorkingBehavior);
        }
    

    The split and combined use of a single responsibility is shown in the example, for reference only.

9. Summary

Whether it is applying design principles or design patterns, the ultimate goal is to improve the readability, scalability, reusability, maintainability, etc. of the code. When we consider whether it is reasonable to apply a certain design principle, we can also use this as the final consideration.

reference:

The foundation of design patterns - six design principles

Single Responsibility Principle

"The Beauty of Design Patterns"

"Re-learning Java Design Patterns"

Guess you like

Origin blog.csdn.net/jun5753/article/details/126861167