“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

The Six principles of design mode

I. What is the single responsibility principle

First, let’s look at the definition of a single responsibility.

SRP. A class should have only one reason to change. A class should have only one reason to change

For a class, there should only be one reason for it to change. There should be only one responsibility. If a class has more than one responsibility, those responsibilities are coupled together. A change in one responsibility may impair or inhibit the class’s ability to perform other responsibilities. This leads to fragile designs. When one responsibility changes, it may affect other responsibilities. In addition, multiple responsibilities are coupled together, affecting reusability. To avoid this, adhere to the single responsibility principle as much as possible.

At the core of the single responsibility principle is decoupling and enhanced cohesion.

Why should we abide by the single responsibility principle?

Usually, we don’t do anything until we know why we’re doing it. So why do we use the single responsibility principle?

1. Improve maintainability and readability. With less responsibility for a class, less complexity means less code, more readability, and more maintainability.

2, improve the maintainability of the system the system is composed of classes, each class is high maintainability, relatively speaking, the whole system is high maintainability. Assuming, of course, that the architecture of the system is in order.

3. Reduce the risk of change The more responsibilities a class has, the greater the possibility of change and the greater the risk of change

If there are multiple things that can change in a class, this design is risky. We try to make sure that only one can change, and that the others change in other classes. The benefits of this are ** increased cohesion and reduced coupling **.

The scope of application of the single responsibility principle

The scope of the single responsibility principle is interface, method and class. Interfaces and methods, it is said, must have a single responsibility, not classes, as long as they conform to the business.

3.1 [Method level] Application of the single responsibility principle

You now have a scenario where you need to change the user name and password for the user. There are several implementations of this function. The first:

/** * Type of operation */
public enum OperateEnum {
    UPDATE_USERNAME,
    UPDATE_PASSWORD;
}

public interface UserOperate {
    void updateUserInfo(OperateEnum type, UserInfo userInfo);
}

public class UserOperateImpl implements UserOperate{
    @Override
    public void updateUserInfo(OperateEnum type, UserInfo userInfo) {
        if (type == OperateEnum.UPDATE_PASSWORD) {
            // Change the password
        } else if(type == OperateEnum.UPDATE_USERNAME) {
            // Change the user name}}}Copy the code

The second way:

public interface UserOperate {
    void updateUserName(UserInfo userInfo);

    void updateUserPassword(UserInfo userInfo);
}

public class UserOperateImpl implements UserOperate {
    @Override
    public void updateUserName(UserInfo userInfo) {
        // Modify the user name logic
    }

    @Override
    public void updateUserPassword(UserInfo userInfo) {
        // Change the password logic}}Copy the code

Consider the difference between these two implementations: The first implementation is differentiated by operation type, which performs different logic. You couple changing your user name with changing your password. If the client passes the wrong type during the operation, an error will occur. The second implementation is the one we recommend. Changing a user name is logically separate from changing a password. Each shall carry out his own duties without interfering with the other. The function is clear and clear.

Thus, the second design is consistent with the single responsibility principle. This is the implementation of the single responsibility principle at the method level.

3.2 [Interface level] Application of single responsibility principle

Let’s assume a scenario, we do housework together, Zhang SAN sweeping the floor, Li Si shopping. Li Si has to cook when he gets back from shopping. How does this logic work?

Methods a

/** * do housework */
public interface HouseWork {
    / / sweep the floor
    void sweepFloor(a);

    / / shopping
    void shopping(a);
}

public class Zhangsan implements HouseWork{
    @Override
    public void sweepFloor(a) {
        / / sweep the floor
    }

    @Override
    public void shopping(a) {}}public class Lisi implements HouseWork{
    @Override
    public void sweepFloor(a) {}@Override
    public void shopping(a) {
        / / shopping}}Copy the code

First, define an interface to do housework, define two methods to sweep the floor and buy vegetables. Three sweeping, to achieve sweeping interface. Li Si buy vegetables, on the realization of the interface to buy vegetables. Then, after buying food, li Si needs to cook, so we need to add a method cooking in the interface class. Both Sam and Sam override this method, but only Sam has a concrete implementation.

The design itself is not reasonable. First of all: Zhang SAN only sweeps the floor, but he needs to rewrite the shopping method, li Si does not need to sweep the floor, but Li Si also has to rewrite the sweeping method. Second: this also does not comply with the open closed principle. To add a type cook, you need to modify 3 classes. This makes it easy to cause unexpected errors when the logic is complex.

The above design does not comply with the principle of single responsibility. Changing one area affects other areas that do not need to be changed.

# # # method 2

/** * do housework */
public interface Hoursework {}public interface Shopping extends Hoursework{
    / / shopping
    void shopping(a);
}

public interface SweepFloor extends Hoursework{
    / / sweep the floor
    void sweepFlooring(a);
}

public class Zhangsan implements SweepFloor{

    @Override
    public void sweepFlooring(a) {
        // Zhang SAN sweeps the floor}}public class Lisi implements Shopping{
    @Override
    public void shopping(a) {
        // Li Si goes shopping}}Copy the code

Instead of defining chores as an interface, it separates sweeping from doing chores. Three sweeping, then three on the implementation of the sweeping interface. Li Si shopping, Li Si on the implementation of the shopping interface. After li Si wants to add a function to cook. Then add a cooking interface, this time only need Li Si implementation cooking interface can be.

public interface Cooking extends Hoursework{ 
    void cooking(a);
}

public class Lisi implements Shopping.Cooking{
    @Override
    public void shopping(a) {
        // Li Si goes shopping
    }

    @Override
    public void cooking(a) {
        // Li Si does the cooking}}Copy the code

As mentioned above, we see that Sam does not implement redundant interfaces, and neither does Sam. And when the new features were added, they only affected Li Si, not Zhang SAN. This is consistent with the single responsibility principle. A class does only one thing. And his changes will bring no other changes.

3.3 [Class level] Application of the single responsibility principle

At the class level, there is no way to completely break it down into a single responsibility. In other words, the responsibilities of a class can be large or small, unlike an interface, which can be clearly split according to a single responsibility principle. As long as it makes logical sense.

For example, we can register, login and wechat login on the homepage of the website. Perform operations such as registration and login. What we usually do is:

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) {
        // User login
    }

    @Override
    public void register(UserInfo userInfo) {
        // User registration
    }

    @Override
    public void logout(UserInfo userInfo) {
        // User logout}}Copy the code

If you split it according to the single responsibility principle, you can also split it into the following form


public interface Register {
    void register(a);
}

public interface Login {
    void login(a);
}

public interface Logout {
    void logout(a);
}


public class RegisterImpl implements Register{

    @Override
    public void register(a) {}}public class LoginImpl implements Login{
    @Override
    public void login(a) {
        // User login}}public class LogoutImpl implements Logout{

    @Override
    public void logout(a) {}}Copy the code

Can I write it like this? It can be, but there are lots of classes. If the login, registration, and logout operations have a lot of code, you can write this.

How to comply with the single responsibility principle

4.1 Reasonable breakdown of responsibilities

Putting the same responsibilities together and dividing them into different interfaces and implementations is one of the easiest and most difficult principles to apply. The key is to identify the same type of responsibilities from a business perspective and from a requirements perspective.

Example: The analysis of human behavior includes the analysis of life and work behaviors. Life behaviors include eating, running, sleeping, etc., and work behaviors include commuting, meeting, etc., as shown in the figure below:

Human behavior is divided into two interfaces: the life behavior interface, the work behavior interface, and two implementation classes. Having a single implementation class for both interfaces leads to bloated code that is not easy to maintain, and risks change if other behaviors, such as learning behavior interfaces, are added later (the composite pattern is also used here).

4.2 Take a look at the simple code implementation

Step 1: Define a behavior interface


Human behavior * Human behavior includes two kinds: life behavior and work behavior */
public interface IBehavior {}Copy the code

It defines an empty interface, the behavior interface. What are the interfaces under this behavior interface? There are both life and work activities.

Step 2: Define the life and work interfaces, and they are subclasses of the behavior interface

Life behavior interface:

public interface LivingBehavior extends IBehavior{
    / * * * / for dinner
    void eat(a);

    Run / * * * /
    void running(a);

    / * * * / sleep
    void sleeping(a);
}

Copy the code

Work behavior interface:

public interface WorkingBehavior extends IBehavior{

    / * * * / work
    void goToWork(a);

    / * * * / work
    void goOffWork(a);

    / * * * / the meeting
    void meeting(a);
}
Copy the code

Step 3: Define the implementation classes of the working behavior interface and the living behavior interface

Life behavior interface implementation class:

public class LivingBehaviorImpl implements LivingBehavior{
    @Override
    public void eat(a) {
        System.out.println("Eat");
    }

    @Override
    public void running(a) {
        System.out.println("Running");
    }

    @Override
    public void sleeping(a) {
        System.out.println("Sleep"); }}Copy the code

Work behavior interface implementation class:

public class WorkingBehaviorImpl implements WorkingBehavior{

    @Override
    public void goToWork(a) {
        System.out.println("Work");
    }

    @Override
    public void goOffWork(a) {
        System.out.println("Work");
    }

    @Override
    public void meeting(a) {
        System.out.println("The meeting"); }}Copy the code

Step 4: Behavior combination invocation.

The behavior interface is defined. We’ll define a set of behaviors. Different users have different behaviors. Some users only have life behaviors, while others have both life behaviors and work behaviors

We don’t know exactly what the user will do, so we usually use a collection to receive the user’s behavior. What the user does, you add to it.

1. The behavior combination interface BehaviorComposer

public interface BehaviorComposer {
    void add(IBehavior behavior);
}
Copy the code

2. The behavior combination interface implements class IBehaviorComposerImpl

public class IBehaviorComposerImpl implements BehaviorComposer {

    private List<IBehavior> behaviors = new ArrayList<>();
    @Override
    public void add(IBehavior behavior) {
        System.out.println("Add behavior");
        behaviors.add(behavior);
    }

    public void doSomeThing(a) {
        behaviors.forEach(b->{
            if(b instanceof LivingBehavior) {
                LivingBehavior li = (LivingBehavior)b;
                // Deal with life behavior
            } else if(b instanceof WorkingBehavior) {
                WorkingBehavior wb = (WorkingBehavior) b;
                // Handle work behavior}}); }}Copy the code

Step 5: Client invocation

When the user calls, it can be called according to the actual situation, such as the following code: Zhang SAN is a full-time mother, only life behavior, Li Si is a working mother, both life behavior and work behavior.

public static void main(String[] args) {
        // Zhang SAN -- a stay-at-home mother
        LivingBehavior zslivingBehavior = new LivingBehaviorImpl();
        BehaviorComposer zsBehaviorComposer = new IBehaviorComposerImpl();
        zsBehaviorComposer.add(zslivingBehavior);

        // Li Si -- a working mother
        LivingBehavior lsLivingBehavior = new LivingBehaviorImpl();
        WorkingBehavior lsWorkingBehavior = new WorkingBehaviorImpl();

        BehaviorComposer lsBehaviorComposer = new IBehaviorComposerImpl();
        lsBehaviorComposer.add(lsLivingBehavior);
        lsBehaviorComposer.add(lsWorkingBehavior);
    }
Copy the code

You can see the benefits of having a single responsibility.

V. Advantages and disadvantages of the single responsibility principle

  • Class complexity reduced: with clearly defined responsibilities for what a class implements, complexity is naturally reduced

  • Improved readability: As complexity decreases, readability naturally increases

  • Improved maintainability: As readability improves, code becomes easier to maintain

  • Risk reduction due to change: Change is essential, if the single responsibility of the interface is done well, an interface change will only affect the corresponding implementation class, not the other interfaces and classes, which will greatly help the scalability and maintainability of the system

4. Best Practices baijiahao.baidu.com/s?id=164624…