[23 Design Patterns] Single Responsibility Principle

Personal homepage:Golden Scales Stepping on the Rain

Personal introduction: Hello everyone, I amJinlin, a fledgling Java novice< /span>

Current situation: A 22nd ordinary undergraduate graduate. After many twists and turns, he now works for a large and well-known domestic daily chemical company, engaged in Java development

My blog: This is CSDN, where I learn technology and summarize knowledge. I hope to communicate with you and make progress together ~

1. How to understand the single responsibility principle?

Single Responsibility Principle (SRP), whichrequires aclass or < /span>improve the readability and maintainability of the code< /span>. and reduce the coupling between classes. This helps should only be responsible for one specific functionModules

We can think of modules as more abstract than classesConcept, classes can also be viewed as modules. Or think of a module as a code block with a coarsergranularity than a class. A module contains multiple classes, and multiple classes make up a module.

The definition and description of the Single Responsibility Principle is very simple and not difficult to understand. A class is only responsible for completing one responsibility or function.

Do not design large and comprehensive classes, but design classes with small granularity and single function. A class contains two or more unrelated functions, then we say that its responsibilities are not single enough, it should be split into multiple classes with more single functions and finer granularity.

case analysis

Suppose we need to implement an employee management system process employee information and salary calculation

An implementation that does not follow the single responsibility principle might look like this:

class Employee {
    private String name;
    private String position;
    private double baseSalary;

    public Employee(String name, String position, double baseSalary) {
        this.name = name;
        this.position = position;
        this.baseSalary = baseSalary;
    }

    // Getter 和 Setter 方法

    // 这些 calculateSalary()、saveEmployee() 最好不要放在这里面。
    public double calculateSalary() {
        // 计算员工工资的逻辑
        return baseSalary * 1.2;
    }

    public void saveEmployee() {
        // 保存员工信息到数据库的逻辑
    }
}

In the above code, the Employee class is responsible for the management of employee information, salary calculation and persistence of employee information, whichviolates the single responsibility principle.

To follow this principle, we can split these functionsinto different classes!

class Employee {
    private String name;
    private String position;
    private double baseSalary;

    public Employee(String name, String position, double baseSalary) {
        this.name = name;
        this.position = position;
        this.baseSalary = baseSalary;
    }

    // Getter 和 Setter 方法
    public double calculateSalary() {
        // 计算员工工资的逻辑
        return baseSalary * 1.2;
    }
}

class EmployeeRepository {
    public void saveEmployee(Employee employee) {
        // 保存员工信息到数据库的逻辑
    }
}

In the code that follows the single responsibility principle, we extract the employee informationpersistence operation from the Employee class and put it in In a new EmployeeRepository class. Now, the Employee class is only responsible for the management of employee information and salary calculation, while the EmployeeRepository class is responsible for the persistence of employee information. In this way,each class only focuses on a specific responsibility, making it easier to understand, maintain and extend.

Following the single responsibility principle helps improve code readability, maintainability, and scalability. Please note that this principle is not absolute, and you need to judge whether you need to split classes and modules based on specific circumstances. Excessive splitting may lead to too many classes and modules, which will increase the complexity of the system.

2. How to judge whether the responsibilities of a class are single enough?

"Single" does not have an absolute standard answer, everything should be based on actual business. Once the business logic of a seemingly single module is large enough, in most cases, it can continue to be subdivided. "Single" is relative!

Judging from the example just now, it seems that the single responsibility principle is not difficult to apply.

But in most cases, are the methods in the class classified as the same type of function, or Classified intotwo types of unrelated functions? It’s not that easy to determine. In real software development, it is difficult to determine whether a class has a single responsibility.

case analysis

In a social product, we use the following UserInfo class to record user information. Do you think the design of the UserInfo class satisfies the single responsibility principle?

public class UserInfo {
    private long userId;
    private String username;
    private String email;
    private String telephone;
    private String avatarUrl;
    private String province; // 省
    private String cityOf; // 市
    private String region; // 区 
    private String detailedAddress; // 详细地址
    // ... 省略其他属性和方法...
}

There are two different views on this issue.

  • One view is that the UserInfo class contains information related to the user, and all attributes and methods belong to the user's business model, satisfying the single responsibility principle;
  • Another view is:Address informationIn the UserInfo class, it accounts for a relatively high proportion, so you can continue< a i=3>Split into an independent Address class. UserInfo only retains other information except Address. The responsibilities of the two classes after the split are more single.

Which view is more correct? In fact, to choose among them, we cannot break away from the specific application scenarios. If in this social product, the user's address information is the same as other information, but is simply used for display< /span>, then the current design of UserInfo is reasonable. However, if this social product develops better and an e-commerce module is added to the product later, the user's address information will also be used in e-commerce. In business logistics, it is best for us to separate the address information from UserInfo and independently form user logistics information (or address information, delivery information, etc.).

So, there is one thing we should remember:To talk about design without business is to be a hooligan. In fact, it is useless to talk about anything without business. It's just a rogue, Technology serves business. This is an eternal truth! ! !

In the software development process, we can first write a coarse-grained class to meet business needs. With the development of business, ifcoarse-grained classes become larger and larger, and there are more and more codes, at this time, we can use this coarse-grained class, split into several more fine-grained classes. This is calledcontinuous refactoring.

3. Are the responsibilities of a class designed to be as simple as possible?

In order to satisfy the single responsibility principle, is it better to split the class into more details?

the answer is negative.

The Serialization class implements the serialization and deserialization functions of a simple protocol. The specific code is as follows:

public class Serialization {
	private static final String IDENTIFIER_STRING = "UEUEUE;";
	private Gson gson;

	public Serialization() {
		this.gson = new Gson();
	}

	// 序列化
	public String serialize(Map<String, String> object) {
		StringBuilder textBuilder = new StringBuilder();
		textBuilder.append(IDENTIFIER_STRING);
		textBuilder.append(gson.toJson(object));
		return textBuilder.toString();
	}

	// 反序列化
	public Map<String, String> deserialize(String text) {
		if (!text.startsWith(IDENTIFIER_STRING)) {
			return Collections.emptyMap();
		}
		String gsonStr = text.substring(IDENTIFIER_STRING.length());
		return gson.fromJson(gsonStr, Map.class);
	}
}

If we want to make the responsibilities of the class more single, we further split the Serialization class into a class that is only responsible for serialization work Serializer class and another Deserializer class that is only responsible for deserialization work.

// 序列化
public class Serializer {
    private static final String IDENTIFIER_STRING = "UEUEUE;";
    private Gson gson;
    
    public Serializer() {
    	this.gson = new Gson();
    }
  
    public String serialize(Map<String, String> object) {
        StringBuilder textBuilder = new StringBuilder();
        textBuilder.append(IDENTIFIER_STRING);
        textBuilder.append(gson.toJson(object));
        return textBuilder.toString();
    }
}

// 反序列化
public class Deserializer {
    private static final String IDENTIFIER_STRING = "UEUEUE;";
    private Gson gson;
    
    public Deserializer() {
    	this.gson = new Gson();
    }
  
    public Map<String, String> deserialize(String text) {
    if (!text.startsWith(IDENTIFIER_STRING)) {
        return Collections.emptyMap();
    }
    String gsonStr = text.substring(IDENTIFIER_STRING.length());
    return gson.fromJson(gsonStr, Map.class);
    }
}

Although after the split, the responsibilities of the Serializer class and Deserializer class have become more single, they have also brought new problems. If we modify the format of the protocol,the data identifier is changed from "UEUEUE" to "DFDFDF", or the serialization methodFrom JSON to XML, the Serializer class and Deserializer class both need to be modified accordingly, the code< /span>. Moreover, if we only make protocol modifications to the Serializer class and forget to modify the code of the Deserializer class, it will lead to mismatches in serialization and deserialization, and program operation errors. In other words, after splitting,. maintainability has become worseThe code'sObviously not as high as the original SerializationCohesion

In fact, no matter whether you apply design principles or design patterns, the ultimate goal is to improve the readability, scalability, reusability, maintainability, etc. of the code. We can also use this as the final criterion when considering whether it is reasonable to apply a certain design principle.

Guess you like

Origin blog.csdn.net/weixin_43715214/article/details/134001660