Six principles of design patterns (quoted from others)

 First, the single responsibility principle
The single responsibility principle is the simplest object-oriented design principle, which is used to control the granularity of classes. The Single Responsibility Principle is defined as follows:
Single Responsibility Principle (SRP): A class is only responsible for the corresponding responsibilities in one functional area, or it can be defined as: For a class, there should be only one reason for it to change.
The Single Responsibility Principle tells us that a class can't be too "tired"! In a software system, the more responsibilities a class (from a module to a method) undertakes, the less likely it is to be reused, and a class has too many responsibilities, which is equivalent to coupling these responsibilities in At the same time, when one of the responsibilities changes, it may affect the operation of other responsibilities. Therefore, these responsibilities should be separated, and different responsibilities should be encapsulated in different classes, that is, different reasons for changes should be encapsulated in different classes. Responsibilities that always change at the same time can be encapsulated in the same class.
The Single Responsibility Principle is a guideline for achieving high cohesion and low coupling. It is the simplest but most difficult principle to apply. It requires the designer to discover the different responsibilities of a class and separate them, and to discover the multiple responsibilities of a class requires the designer to have Strong analysis and design skills and relevant practical experience.
The following is a simple example to further analyze the single responsibility principle:
The developers of Sunny software company put forward the initial design scheme as shown in the following figure for the customer information graphic statistics module in a CRM (Customer Relationship Management, customer relationship management) system:

Analyze the existing CustomerDataChart class, which takes on too many responsibilities and contains both database-related methods and methods related to chart generation and display. If other classes also need to connect to the database or use the findCustomers() method to query customer information, it is difficult to reuse the code. Whether modifying the database connection mode or modifying the chart display mode, the class needs to be modified. It has more than one reason for its change, which violates the single responsibility principle. Therefore, it is necessary to split this class to make it meet the principle of single responsibility. The class CustomerDataChart can be split into the following three classes:
(1) DBUtil: responsible for connecting to the database, including the database connection method getConnection();
(2) CustomerDAO: responsible for Operate the Customer table in the database, including adding, deleting, modifying, and querying the Customer table, such as findCustomers();
(3) CustomerDataChart: Responsible for the generation and display of charts, including the methods createChart() and displayChart().

The structure after refactoring using the single responsibility principle is shown in the following figure:



 Second, the open-closed principle
The open-closed principle is the first cornerstone of object-oriented reusable design, and it is the most important object-oriented design principle. The Open-Closed Principle was proposed by Bertrand Meyer in 1988 and is defined as follows:
Open-Closed Principle (OCP): A software entity should be open for extension and closed for modification. That is, software entities should try their best to expand without modifying the original code.
In the definition of the open-closed principle, a software entity can refer to a software module, a local structure composed of multiple classes, or an independent class.
An important issue that any software needs to face is that their requirements will change over time. When the software system needs to face new requirements, we should try our best to ensure that the design framework of the system is stable. If a software design conforms to the open-closed principle, the system can be easily extended without modifying the existing code, so that the software system has better stability and continuity while having adaptability and flexibility. . With the increasing scale of software, longer software life, and higher software maintenance costs, it becomes more and more important to design software systems that satisfy the open-closed principle.
In order to satisfy the open-closed principle, the system needs to be abstractly designed, and abstraction is the key to the open-closed principle. In programming languages ​​such as Java and C#, a relatively stable abstraction layer can be defined for the system, and different implementation behaviors can be moved to a specific implementation layer. In many object-oriented programming languages, mechanisms such as interfaces and abstract classes are provided, through which the abstract layer of the system can be defined and then extended through concrete classes. If you need to modify the behavior of the system, you don't need to make any changes to the abstraction layer. You only need to add new concrete classes to implement new business functions, and to expand the functions of the system without modifying the existing code, so as to achieve the open-closed principle. requirements.
The CRM system developed by Sunny Software Company can display various types of charts, such as pie charts and bar charts, etc. In order to support a variety of chart display methods, the original design scheme is shown in the following figure:
 

 There is a display() method in the ChartDisplay class. The following code snippet:

......
if (type.equals("pie")) {
    PieChart chart = new PieChart();
    chart.display();
}
else if (type.equals("bar")) {
    BarChart chart = new BarChart();
    chart.display();
}
......

In this code, if you need to add a new chart class, such as the line chart LineChart, you need to modify the source code of the display() method of the ChartDisplay class to add new judgment logic, which violates the open-close principle.

The system is now reconstructed to conform to the open-closed principle.

In this example, since each chart class is programmed in the display() method of the ChartDisplay class, the source code has to be modified to add a new chart class. The system can be refactored in an abstract way, so that it is not necessary to modify the source code when adding a new chart class, which satisfies the open-closed principle. The specific methods are as follows:
(1) Add an abstract chart class AbstractChart, and use various concrete chart classes as its subclasses;
(2) The ChartDisplay class is programmed for the abstract chart class, and the client decides which specific chart to use.
The structure after reconstruction is shown in the following figure:



In the above figure, we have introduced the abstract chart class AbstractChart, and ChartDisplay is programmed for the abstract chart class, and the instantiated concrete chart object is set by the client through the setChart() method, and the chart is called in the display() method of ChartDisplay The object's display() method displays the chart. If you need to add a new chart, such as a line chart LineChart, you only need to use LineChart as a subclass of AbstractChart, and inject a LineChart object into ChartDisplay on the client side, without modifying the source code of the existing class library.

Note: Because the configuration files in the format of xml and properties are plain text files, they can be edited directly through VI editor or Notepad without compiling. Therefore, in software development, the modification of configuration files is generally not considered to be a change to the system. Modification of source code. If a system only involves modifying the configuration file when expanding, and the original Java code or C# code has not been modified, the system can be considered as a system that conforms to the open-closed principle.

3.
Liskov Substitution Principle The Liskov Substitution Principle was proposed in 1994 by Professor Barbara Liskov, the winner of the Turing Award in 2008 and the first female doctor of computer science in the United States, and Professor Jeannette Wing of Carnegie Mellon University. It is strictly stated as follows: if for every object o1 of type S, there is an object o2 of type T, so that all programs P defined by T have no change in the behavior of program P when all objects o1 are substituted for o2, Then type S is a subtype of type T. This definition is rather awkward and difficult to understand, so we generally use another popular version of it:
Liskov Substitution Principle (LSP): all references to a base class (parent class) must be able to use its children transparently object of class.
The Liskov substitution principle tells us that if a base class object is replaced by its subclass object in software, the program will not generate any errors and exceptions, and vice versa, if a software entity uses a subclass object If so, then it may not be able to use the base class object. For example: if I like animals, then I must like dogs, because dogs are a subclass of animals; but I like dogs, so I can't conclude that I like animals, because I don't like mice, even though they are animals.
For example, if there are two classes, one is BaseClass, the other is SubClass, and SubClass is a subclass of BaseClass, then a method can accept a base class object base of BaseClass type, such as: method1(base), Then it must accept a subclass object sub of the BaseClass type, and method1(sub) can run normally. The reverse substitution does not hold. For example, a method method2 accepts a subclass object sub of the BaseClass type as a parameter: method2(sub), then in general, there can be no method2(base) unless it is an overloaded method.
The Liskov substitution principle is one of the important ways to realize the open-closed principle. Since subclass objects can be used wherever the base class object is used, the base class type should be used as much as possible to define the object in the program, and then the object should be defined at runtime. Determine its subclass type and replace the superclass object with the subclass object.
When using the Liskov substitution principle, pay attention to the following issues:
(1) All methods of the subclass must be declared in the superclass, or the subclass must implement all the methods declared in the superclass. According to the Liskov substitution principle, in order to ensure the scalability of the system, the parent class is usually used to define the program. If a method only exists in the subclass, and the corresponding declaration is not provided in the parent class, it cannot be used in the parent class. This method is used in the defined object.
(2) When we apply the Liskov substitution principle, we try to design the parent class as an abstract class or interface, let the child class inherit the parent class or implement the parent interface, and implement the methods declared in the parent class. At runtime, the child class Instance replaces the instance of the parent class, we can easily extend the function of the system, and at the same time do not need to modify the code of the original subclass, adding new functions can be achieved by adding a new subclass. The Liskov substitution principle is one of the concrete realization means of the open-closed principle.
(3) In the Java language, during the compilation phase, the Java compiler will check whether a program complies with the Liskov substitution principle. This is an implementation-independent, purely syntactic check, but the Java compiler's check is limited. of.
In the CRM system developed by Sunny Software Company, customers can be divided into two categories: VIP customers (VIPCustomer) and common customers (CommonCustomer). The system needs to provide a function of sending Email. The original design scheme is shown in the following figure:



After further analysis of the system, it is found that the process of sending emails is the same whether it is an ordinary customer or a VIP customer, that is to say, the codes in the two send() methods are repeated, and new types will be added in this system. customer of. In order to make the system more scalable and reduce code duplication, it is refactored using the Liskov substitution principle.
In this example, we can consider adding a new abstract customer class Customer, and use CommonCustomer and VIPCustomer as its subclasses, and the email sending class EmailSender class is programmed for the abstract customer class Customer. According to the Liskov substitution principle, it can accept the base class The object must be able to accept subclass objects, so change the parameter type of the send() method in EmailSender to Customer. If you need to add a new type of customer, you only need to use it as a subclass of the Customer class. The reconstructed structure is shown in the following figure:



The Liskov substitution principle is one of the important ways to realize the open-closed principle. In this example, the base class object is used when passing parameters. In addition, the Liskov substitution principle can be used when defining member variables, defining local variables, and determining the return type of a method. For base class programming, specific subclasses are determined when the program is running.

4. Dependency Inversion Principle

If the open-closed principle is the goal of object-oriented design, then the dependency inversion principle is one of the main realization mechanisms of object-oriented design, and it is the concrete realization of system abstraction. The Dependency Inversion Principle is the third volume in Robert C. Martin's 1996 column Engineering Notebook for "C++Reporter" and later added to his classic 2002 book "Agile Software Development, Principles, Patterns, and Practices" book. The Dependency Inversion Principle is defined as follows:
Dependency Inversion Principle (DIP): Abstraction should not depend on details, and details should depend on abstraction. In other words, program for the interface, not for the implementation.
The Dependency Inversion Principle requires us to refer to high-level abstract classes as much as possible when passing parameters in program code or in association relationships, that is, using interfaces and abstract classes for variable type declarations, parameter type declarations, method return type declarations, and data types instead of using concrete classes to do these things. In order to ensure the application of this principle, a concrete class should only implement the methods declared in the interface or abstract class, and not give redundant methods, otherwise it will not be able to call the new methods added in the subclass.
After the introduction of the abstraction layer, the system will have good flexibility, try to use the abstraction layer for programming in the program, and write the concrete classes in the configuration file. In this way, if the system behavior changes, only the abstraction layer needs to be changed. Expand and modify the configuration file without modifying the source code of the original system, expand the function of the system without modification, and meet the requirements of the open-closed principle.
When implementing the Dependency Inversion Principle, we need to program against the abstract layer, and inject objects of concrete classes into other objects by means of Dependency Injection (DI). Dependency injection means that when an object has a dependency relationship with other objects When injecting dependent objects through abstraction. There are three commonly used injection methods, namely: construction injection, setter injection (Setter injection) and interface injection. Constructive injection refers to passing in the object of a specific class through the constructor, setting value injection refers to passing in the object of the specific class through the Setter method, and interface injection refers to passing in the specific class through the business method declared in the interface object. These methods use an abstract type when they are defined, and then pass in an object of a concrete type at runtime, and the subclass object overrides the superclass object.
The following is a simple example to deepen the understanding of the principle of dependency inversion:
When developing a CRM system, the developers of Sunny software company found that the system often needs to transfer the customer information stored in TXT or Excel files to the database, so it needs to Perform data format conversion. In the customer data operation class, the method of the data format conversion class will be called to realize the format conversion and database insertion operation. The initial design scheme structure is shown in the following figure:



When coding to realize the structure shown in the figure above, the developers of Sunny software company found that there is a very serious problem in the design. Since the data source is not necessarily the same every time the data is converted, it is necessary to replace the data conversion class. TXTDataConvertor is changed to ExcelDataConvertor. At this time, the source code of CustomerDAO needs to be modified, and the source code of CustomerDAO has to be modified when introducing and using a new data conversion class. The system has poor scalability and violates the open-closed principle. The program is refactored.
In this example, since CustomerDAO is programmed for specific data conversion classes, the source code of CustomerDAO has to be modified when adding new data conversion classes or replacing data conversion classes. We can solve this problem by introducing the abstract data conversion class. After the introduction of the abstract data conversion class DataConvertor, CustomerDAO is programmed for the abstract class DataConvertor, and the specific data conversion class name is stored in the configuration file, which conforms to the principle of dependency inversion. According to the Liskov substitution principle, when the program is running, the specific data conversion class object will replace the object of the DataConvertor type, and the program will not have any problems. When replacing a specific data conversion class, it is not necessary to modify the source code, only the configuration file needs to be modified; if a new specific data conversion class needs to be added, just use the new data conversion class as a subclass of DataConvertor and modify the configuration file, and the original code does not need to be Make any modifications to satisfy the open-closed principle. The reconstructed structure is shown in the following figure:



In the above refactoring process, we used the Open-Closed Principle, the Liskov Substitution Principle and the Dependency Inversion Principle. In most cases, these three design principles will appear at the same time. The Open-Closed Principle is the goal, and the Liskov Substitution Principle It is the foundation, and the Dependency Inversion Principle is the means. They complement each other, complement each other, and have the same goal, but the angle of analysis is different.

Five, interface isolation principle

The interface isolation principle is defined as follows:

Interface Segregation Principle (ISP): Use multiple specialized interfaces instead of a single general interface, i.e. the client should not depend on those interfaces it does not need.
According to the principle of interface isolation, when an interface is too large, we need to divide it into some smaller interfaces, and clients using this interface only need to know the methods related to it. Each interface should assume a relatively independent role, not to do what it should not do, but to do what it should do. The "interface" here often has two different meanings: one refers to the collection of method characteristics possessed by a type, which is only a logical abstraction; the other refers to the specific "interface" definition of a certain language , with strict definitions and structures, such as the interface in the Java language. For these two different meanings, the expression and meaning of ISP are different:
(1) When "interface" is understood as a collection of all method features provided by a type, this is a logical concept , the division of interfaces will directly lead to the division of types. Interfaces can be understood as roles. An interface can only represent one role, and each role has its own specific interface. At this time, this principle can be called the "role isolation principle".
(2) If the "interface" is understood as an interface in a specific language in a narrow sense, then the meaning expressed by ISP means that the interface only provides the behavior that the client needs, and the behavior that the client does not need is hidden, and the client should be provided as much as possible. Small individual interfaces instead of large total interfaces. In object-oriented programming languages, implementing an interface requires implementing all the methods defined in the interface. Therefore, it is not necessarily convenient to use a large general interface. In order to make the responsibility of the interface single, it is necessary to classify the methods in the large interface according to their responsibilities. The differences are placed in different small interfaces to ensure that each interface is more convenient to use and assumes a single role. The interface should be as detailed as possible, and the methods in the interface should be as few as possible. Each interface only contains the methods required by a client (such as a submodule or business logic class). This mechanism is also called "custom service". , that is, to provide different interfaces with different widths for different clients.
The following is a simple example to deepen the understanding of the principle of interface isolation:
The developers of Sunny software company designed the interface as shown in the following figure for the customer data display module of a CRM system, in which the method dataRead() is used to read data from the file, the method transformToXML() is used to convert the data into XML format, and the method createChart() is used to create a chart, the method displayChart() is used to display the chart, the method createReport() is used to create a text report, and the method displayReport() is used to display a text report.



In actual use, it is found that this interface is very inflexible. For example, if a specific data display class does not need to perform data conversion (the source file itself is in XML format), but because this interface is implemented, it will have to implement transformToXML() declared in it. method (at least one empty implementation needs to be provided); if you need to create and display a chart, in addition to the methods related to the chart, you also need to implement the method of creating and displaying text reports, otherwise an error will be reported when the program is compiled.
It is now refactored using the interface segregation principle.
In the above figure, because too many methods are defined in the interface CustomerDataDisplay, that is, the interface undertakes too many responsibilities, on the one hand, the implementation class of the interface is very large, and different implementation classes have to implement the methods defined in the interface. All methods have poor flexibility. If there are a large number of empty methods, a large number of useless codes will be generated in the system, which will affect the code quality. , the client sees a method it shouldn't see, and the interface is not customized for the client. Therefore, it is necessary to reconstruct the interface according to the principle of interface isolation and single responsibility, and encapsulate some of the methods in different small interfaces to ensure that each interface is more convenient to use and assumes a single role. An interface contains only the methods required by a client (such as a module or class).
By using the interface isolation principle, the reconstructed structure of this instance is shown in the following figure:



When using the interface isolation principle, we need to pay attention to the granularity of the control interface. The interface should not be too small. If it is too small, it will lead to the flooding of interfaces in the system, which is not conducive to maintenance; the interface should not be too large, and the interface that is too large will violate the interface isolation principle. Less flexibility and inconvenient to use. In general, an interface should only contain methods customized for a certain class of users, and clients should not be forced to rely on methods they do not use.

6.
The Law of Demeter The Law of Demeter comes from a research project called "Demeter" at Northeastern University in the United States in 1987. The Law of Demeter, also known as the LeastKnowledge Principle (LKP), is defined as follows:
Law of Demeter (LoD): A software entity should interact with other entities as little as possible.
If a system complies with the Law of Demeter, then when one of the modules is modified, it will affect other modules as little as possible, and the expansion will be relatively easy. This is a restriction on the communication between software entities, and the Law of Demeter requires restrictions The breadth and depth of communication between software entities. The Law of Demeter can reduce the coupling degree of the system and keep loose coupling between classes.
There are several other definitions of the Law of Demeter, including: don't talk to "strangers", only communicate with your direct friends, etc. In the Law of Demeter, for an object, its friends include the following categories:
(1 ) The current object itself (this);
(2) The object passed into the current object method as a parameter;
(3) The member object of the current object;
(4) If the member object of the current object is a set, then the Elements are also friends;
(5) The object created by the current object.
Any object that meets one of the above conditions is a "friend" of the current object, otherwise it is a "stranger". When applying the Law of Demeter, an object can only interact with direct friends, and do not interact directly with "strangers". This can reduce the coupling of the system, and changes in one object will not bring too many other objects. influence.
The Law of Demeter requires us to design systems in such a way that the interaction between objects should be minimized. If two objects do not have to communicate directly with each other, then the two objects should not have any direct interaction, if one of them If an object needs to call a method of another object, the call can be forwarded through a third party. In short, it is to reduce the coupling between existing objects by introducing a reasonable third party.
When applying the Law of Demeter to system design, pay attention to the following points: In the division of classes, you should try to create loosely coupled classes. The lower the coupling between classes, the more conducive to reuse. Once a loosely coupled class is modified, it will not affect the associated classes too much; in the structural design of the class, each class should try to reduce the access rights of its member variables and member functions; By design, whenever possible, a type should be designed as an immutable class; in terms of references to other classes, an object's references to other objects should be minimized.
The following is a simple example to deepen the understanding of Demeter's Law:
The CRM system developed by Sunny Software Company contains many business operation windows. In these windows, there are complex interaction relationships between certain interface controls. The triggering of a control event Will cause multiple other interface controls to respond, for example, when a button (Button) is clicked, the corresponding list box (List), combo box (ComboBox), text box (TextBox), text label (Label), etc. Changes will occur. In the initial design scheme, the interaction between interface controls can be simplified as shown in the following figure:




 
 

Guess you like

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