1.1 Concept and significance of software design pattern
There are many definitions of software design patterns, some from the characteristics of patterns, some from the role of patterns. The definitions given in this tutorial are generally accepted by most scholars and are illustrated in two ways.
1. The concept of software design patterns
Software Design Pattern, also known as Design Pattern, is a set of repeatedly used, most people know, after classification and cataloging, code Design experience summary. It describes some recurring problems in the software design process, and the solutions to this problem. That is to say, it is a series of routines to solve a specific problem. It is a summary of the code design experience of predecessors. It has a certain universality and can be used repeatedly. The goal is to improve code reusability, code readability, and code reliability.
2. Learn the meaning of design patterns
The essence of design pattern is the practical application of object-oriented design principles, and it is a full understanding of the encapsulation, inheritance and polymorphism of classes, as well as the association and combination of classes. Using design patterns correctly has the following advantages.
Can improve the programmer’s thinking ability, programming ability and design ability.
Make the program design more standardized, code preparation more engineering, so that the software development efficiency is greatly improved, thus shortening the software development cycle.
So that the design of the code can be reused, readability, high reliability, good flexibility, maintainability.
Of course, software design patterns are only a guide. In the specific software development, it must be chosen appropriately according to the characteristics and requirements of the designed application system. For simple program development, it may be easier to write a simple algorithm than to introduce a design pattern. But for large project development or framework design, it’s better to use design patterns to organize your code.
1.2 Classification of design patterns
There are two ways to classify design patterns, namely, according to the purpose of the pattern and according to the scope of the pattern.
1. Divide by purpose
Patterns can be divided into creation patterns, structural patterns and behavioral patterns according to what they are used to accomplish.
The creation pattern describes how objects are created. Its main feature is the separation of object creation and use. GoF provides five creation patterns: singleton, prototype, factory method, Abstract factory, and Builder.
Structural patterns: Used to describe how classes or objects are arranged into a larger structure. GoF provides seven structural patterns: proxy, adapter, bridge, decoration, facade, share, and composition.
Behavioral patterns: Describe how classes or objects work together to accomplish tasks that a single object could not accomplish alone, and how responsibilities are assigned. GoF provides 11 behavioral patterns, including template methods, policies, commands, chains of responsibility, states, observers, mediators, iterators, visitors, memos, and interpreters.
2. Divide according to scope of action
Patterns can be divided into class patterns and object patterns according to whether they are mainly used on classes or objects.
Class pattern: Used to deal with relationships between classes and subclasses, which are established by inheritance and are static and determined at compile time. Factory methods, (class) adapters, template methods, and interpreters in GoF belong to this pattern.
Object pattern: Used to deal with relationships between objects, which can be implemented through composition or aggregation, that can change at run time and be more dynamic. All but four of the above are object modes in GoF.
Table 1 describes the classification of these 23 design patterns
Scope, purpose | Creation pattern | Structural mode | Behavioral pattern |
---|---|---|---|
Class model | The factory method | (Class) adapter | Template methods, interpreters |
Object pattern | Singleton prototype abstract factory builder | The broker (object) adapter Bridges the decorative appearance share element combination | Policy command responsibility Chain state observer mediator iterator visitor memo |
3. Features of GoF’s 23 design modes
The 23 categories of GoF design patterns were described earlier, and now the functionality of each pattern is described. Singleton pattern: Only one instance of a class can be generated. This class provides a global access point for external access to the instance. The extension is the finite-multiple pattern.
Prototype pattern: Use an object as a Prototype and clone it to create new instances similar to the Prototype.
Factory Method pattern: Defines an interface for creating products, and subclasses decide what products to produce.
AbstractFactory pattern: provides an interface to create a family of products, each subclass of which can produce a series of related products.
Builder pattern: Decompose a complex object into relatively simple parts, then create them separately according to different needs, and finally build the complex object.
Proxy mode: Provides a Proxy for an object to control access to that object. That is, a client indirectly accesses an object through a proxy to restrict, enhance, or modify some of its features.
Adapter pattern: Converts the interface of one class into another interface that the customer expects, making classes that would otherwise not work together due to interface incompatibility work together.
Bridge pattern: Separate the abstraction from the implementation so that they can vary independently. It is realized by using combinatorial relation instead of inheritance relation, thus reducing the coupling degree of abstraction and realization of these two variable dimensions.
Decorator pattern: Dynamically add some responsibility to an object, that is, add additional functionality.
Facade pattern: Provides a consistent interface to multiple complex subsystems, making them more accessible.
Flyweight pattern: Sharing techniques are used to efficiently support reuse of large numbers of fine-grained objects.
Composite pattern: Groups objects into a tree-like hierarchy, giving users consistent access to individual and Composite objects.
TemplateMethod pattern: define the skeleton of an algorithm in an operation and defer some steps of the algorithm to subclasses so that subclasses can redefine specific steps of the algorithm without changing the structure of the algorithm.
Strategy pattern: A series of algorithms are defined and each algorithm is encapsulated so that they are interchangeable and the change of the algorithm does not affect the customers using the algorithm.
Command pattern: Encapsulates a request as an object, separating the responsibility for making the request from the responsibility for executing the request.
Chain of Responsibility pattern: Requests are passed from one object in the Chain to the next until the request is responded to. In this way, objects are decoupled.
State mode: Allows an object to change its behavior when its internal State changes.
The Observer pattern: There is a one-to-many relationship between objects. When one object changes, the change is notified to the other objects, thereby affecting the behavior of the other objects.
Mediator mode: Define an intermediary object to simplify the interaction between the original objects, reduce the degree of coupling between the objects in the system, so that the original objects do not need to understand each other.
The Iterator pattern: Provides a way to access a series of data in an aggregate object sequentially without exposing the internal representation of the aggregate object.
Visitor pattern: Provides multiple access to each element in a collection without changing the collection elements, that is, multiple Visitor objects for each element.
Memento pattern: Retrieves and saves the internal state of an object so that it can be restored later without breaking encapsulation.
The Interpreter pattern: Provides the grammar of how to define a language and the method of interpreting language sentences, called the Interpreter.
【 Note 】 These 23 design patterns do not exist in isolation, many of them are related to each other, and multiple design patterns are often used simultaneously in large system development. Of course, we’ll also discuss another class of design patterns: the J2EE design pattern.
Table 2.
J2EE patterns | MVC Pattern (MVC Pattern) |
---|---|
These design patterns pay particular attention to the presentation layer. These patterns were identified by the Sun Java Center. | Business Delegate Pattern Composite Entity Pattern Data Access Object Pattern Front Controller Pattern Intercepting Filter Pattern Service Locator Pattern Transfer Object Pattern The Pattern) |
Figure 1 Relationships between design patterns
1.3 The Seven Principles of Design Patterns
1.3.1 Open Close Principle
The Open Closed Principle (OCP) was developed by Bertrand Meyer, In his 1988 book Object Oriented Software Construction, he proposed: Software entities should be open for extension, but closed for modification. This is the classic definition of the open closed principle.
The open closed principle means: open for extensions, closed for modifications. When the program needs to be extended, you cannot modify the original code to achieve a hot plug effect. In short, to make the program extensible, easy to maintain and upgrade. To achieve this, we need to use interfaces and abstract classes, which will be discussed later in the concrete design.
The software entity here consists of the following parts:
The project divides the module into a program which acts according to the interface principle
The function of the open close principle
The open and close principle is the ultimate goal of object-oriented programming, which makes software entities have certain adaptability and flexibility as well as stability and continuity. Specifically, its functions are as follows:
1. Impact on software testing If the software follows the open and closed principle, only the extended code needs to be tested during software testing, because the original test code still works properly.
2. Can improve the reusability of code the smaller the granularity, the greater the likelihood of reuse; In object-oriented programming, programming according to atoms and abstractions can improve code reusability.
3. Can improve the maintainability of software to comply with the open and closed principle of software, its high stability and continuity, thus easy to expand and maintain.
The implementation method of the open and closed principle can be realized by “abstract constraints and encapsulate changes”, that is, by defining a relatively stable abstraction layer for software entities through interfaces or abstract classes, and encapsulating the same variable factors in the same concrete implementation class.
Because abstraction has good flexibility and wide adaptability, as long as abstraction is reasonable, it can basically maintain the stability of software architecture. The changeable details in software can be extended from the abstract implementation class. When the software needs to change, it only needs to derive an implementation class to extend according to the requirements.
The following uses Windows desktop themes as an example to describe how to apply the on/off rule. Windows desktop theme design. Analysis: A Windows theme is a combination of elements such as desktop background images, window colors and sounds. Users can change their own desktop theme according to their favorite, or download a new theme from the Internet. These topics have common characteristics, and you can define an Abstract Subject for them, with each Specific Subject as a subclass. The user form can select or add new themes as needed without modifying the original code, so it meets the open and closed principle, as shown in Figure 2.
Figure 2 Desktop theme class diagram for Windows
1.3.2 Liskov Substitution Principle
Liskov Substitution Principle, LSP was proposed by Ms. Liskov of the MIT Computer Science Laboratory in a paper called “Data Abstraction and Hierarchy” published at the 1987 OOPSLA Summit, She asked: Inheritance should ensure that any property proved about supertype objects also holds for subtypes Objects).
Richter’s substitution principle is one of the basic principles of object-oriented design. Richter’s rule of substitution says that wherever a base class can appear, a subclass must appear. LSP is the cornerstone of inheritance reuse. The base class can be reused only when the derived class can replace the base class and the function of the software unit is not affected. The derived class can also add new behaviors on the base class. Richter’s substitution principle is a complement to the open – close principle. Abstract is the key step to realize the open and close principle, and the inheritance relationship between base class and subclass is the concrete realization of abstraction, so the Richter substitution principle is the specification of the concrete steps to realize abstraction.
To implement the Richter’s Substitution principle, a subclass can extend the functions of its parent class, but cannot change the original functions of its parent class. In other words, when a subclass inherits from a parent class, try not to override the methods of the parent class, except to add new methods to accomplish new functions.
If the method of rewriting the parent class to complete the new function, although it is simple to write, but the reusability of the whole inheritance system will be relatively poor, especially when the use of more frequent polymorphism, the probability of program running error will be very large.
If a program violates the Richter substitution principle, an object that inherits from a class will get a runtime error where the base class appears. At this time, the correction method is to cancel the original inheritance relationship and redesign the relationship between them.
The most famous example of Richter’s substitution is “a square is not a rectangle”. Of course, there are many similar examples in life, for example, penguins, ostriches and kiwis are biologically divided, they belong to birds; However, from the perspective of class inheritance, they cannot be defined as a subclass of “bird” because they cannot inherit the function of “bird” to fly. Similarly, “balloon fish” cannot swim, so it cannot be subclassed as “fish”; “Toy gun” does not blow up enemies, so it cannot be defined as a subclass of “gun”.
The following example illustrates the Richter substitution principle: a Kiwi is not a bird.
Application of Richter’s substitution principle in the case of “a Kiwi is not a bird”. Analysis: Birds generally can fly, for example, the speed of the swallow is about 120 kilometers per hour. But kiwis in New Zealand are unable to fly due to degenerated wings. Suppose you wanted to design an example of how long it would take these two birds to fly 300 kilometers. Obviously, testing the code with swallow gave the correct result for calculating the time required; However, when the kiwi is tested, the result will be “division by zero anomaly” or “infinity”, which obviously does not meet the expectation, as shown in the figure for its class diagram.
Figure 3 class diagram of the “Kiwi is not a Bird” instance
The program code is as follows:
package principle;
public class LSPtest
{
public static void main(String[] args)
{
Bird bird1=new Swallow();
Bird bird2=new BrownKiwi();
bird1.setSpeed(120);
bird2.setSpeed(120);
System.out.println("If you fly 300 kilometers:");
try
{
System.out.println("Swallows will fly."+bird1.getFlyTime(300) +"Hours.");
System.out.println("The Kiwi will fly."+bird2.getFlyTime(300) +"Hours.");
}
catch(Exception err)
{
System.out.println("An error has occurred!); }}}/ / birds
class Bird
{
double flySpeed;
public void setSpeed(double speed)
{
flySpeed=speed;
}
public double getFlyTime(double distance)
{
return(distance/flySpeed); }}/ / the swallow
class Swallow extends Bird{}
// Kiwi birds
class BrownKiwi extends Bird
{
public void setSpeed(double speed)
{
flySpeed=0; }}Copy the code
The running results of the program are as follows:
If it flies 300 kilometers: the swallow will fly 2.5 hours. The kiwi will fly Infinity hours.Copy the code
The reason for the error is that the Kiwi has overwritten the bird setSpeed(double speed) method, which violates the Richter’s substitution principle. Instead: cancel the original kiwi inheritance and define birds and kiwis’ more general parents, such as animals, that are capable of running. Although the flying speed of the kiwi is 0, the running speed is not 0, so the time it takes to run 300 kilometers can be calculated. Its class diagram is shown in the figure.
Figure 4 class diagram of the “Kiwi is an animal” instance
1.3.3 Dependence Inversion Principle
Dependence Inversion Principle (DIP) is an article by Robert c. Martin, president of Object Mentor, published in C++ Report in 1996.
The dependency inversion principle was originally defined as: a high-level module should not depend on a low-level module, but both should depend on its abstraction; Abstraction should not depend on details, Details should depend upon abstractions (High level modules shouldnot depend upon low level modules. Both should depend upon abstractions. Abstractions Should not depend upon details. Details should depend upon Abstractions). The core idea is: program to the interface, not to the implementation.
Dependency inversion principle is one of the important ways to realize the open closed principle, which reduces the coupling between the customer and the implementation module.
Because in software design, detail is variable and abstraction layers are relatively stable, an architecture based on abstraction is much more stable than one based on detail. Here, abstraction refers to an interface or abstract class, while detail refers to a concrete implementation class.
The purpose of using interfaces or abstract classes is to create specifications and contracts that do not involve any concrete operations, leaving the task of presenting the details to their implementation classes. This principle is the basis of the open closed principle, the concrete content: programming for interfaces that rely on abstraction rather than on concrete.
The function of dependence and inversion principle
The main functions of the dependency inversion principle are as follows:
The principle of dependence inversion can reduce the coupling between classes. A system can improve its stability by relying on the principle of inversion. Relying on the principle of inversion reduces the risks associated with parallel development. Relying on inversion principles boosts readability and maintainability of code.
The purpose of the dependency inversion principle is to reduce the coupling between classes by programming to the interface, so we can satisfy this rule in projects by following the following four points in real programming.
Anyway, each class tries to provide an interface, an abstract class, or both. A variable declares itself either as an interface or as an abstract class. Anyway, no class should derive from a concrete class. Anyway, use inheritance to adhere to the Richter substitution principle.
The application of dependency inversion principle is illustrated by the example of “customer shopping program”. Application of dependency inversion principle in “customer shopping program”. Analysis: This program reflects the relationship between “customer class” and “store class”. In the shop class, there is a method called sell(), which is used by the customer class to shop. The following code defines the customer class to shop in ShaoguanShop:
class Customer
{
public void shopping(ShaoguanShop shop)
{
/ / shoppingSystem. The out. Println (shop. Sell ()); }}Copy the code
However, there are drawbacks to this design. If the customer wants to shop from another store (such as WuyuanShop), the customer’s code needs to be modified as follows:
class Customer
{
public void shopping(WuyuanShop shop)
{
/ / shoppingSystem.out.println(shop.sell()); }}Copy the code
Customers change the code every time they change stores, which is a clear violation of the open and close principle. The reason for the above shortcomings is that the customer class is bound to the specific store class when it is designed, which violates the dependency inversion principle. The solution is to define the common interface Shop of “Wuyuan Online Shop” and “Shaoguan Online Shop”. The customer class is programmed to this interface, and the code is modified as follows:
class Customer
{
public void shopping(Shop shop)
{
/ / shoppingSystem.out.println(shop.sell()); }}Copy the code
In this way, no matter what store the Customer class visits, or if a new store is added, there is no need to modify the original code, as shown in the class diagram.
Figure 5. Class diagram of customer shopping program
The program code is as follows:
package principle;
public class DIPtest
{
public static void main(String[] args)
{
Customer wang=new Customer();
System.out.println("Customers purchase the following items:");
wang.shopping(new ShaoguanShop());
wang.shopping(newWuyuanShop()); }}/ / store
interface Shop
{
public String sell(a); / / sell
}
// Shaoguan online shop
class ShaoguanShop implements Shop
{
public String sell(a)
{
return Shaoguan local specialties: mushrooms, agaric......; }}// Wuyuan Online shop
class WuyuanShop implements Shop
{
public String sell(a)
{
return "Wuyuan local specialties: green tea, fish with distiller's grains..."; }}/ / the customer
class Customer
{
public void shopping(Shop shop)
{
/ / shoppingSystem.out.println(shop.sell()); }}Copy the code
The running results of the program are as follows:
Customers buy the following goods: Shaoguan local specialties: mushrooms, agaric...... Wuyuan local specialties: green tea, distiller's grains fish...Copy the code
1.3.4 Single Responsibility Principle
Single Responsibility Principle (SRP), also known as Single function Principle, was formulated by Robert C. Robert C. Martin in Agile Software Development: Principles, Patterns, and Practices. The single responsibility principle states that There should never be more than one reason for a class to change.
This principle states that objects should not take on too many responsibilities. If an object takes on too many responsibilities, there are at least two disadvantages:
Changing a responsibility, either reduces or suppresses the class’s ability to implement other responsibilities; Anyway, when a client needs one responsibility for an object, it has to incorporate all the other responsibilities it doesn’t need, resulting in redundant code or wasted code.
Advantages of the single responsibility principle The core of the single responsibility principle is to control the granularity of classes, decouple objects, and improve their cohesion. Following the single responsibility principle has the following advantages.
Buy a way to reduce the complexity of a class. The logic of a class having one responsibility is certainly much simpler than having multiple responsibilities. Queues improve the readability of classes. As complexity decreases, readability increases. Lends to improve system maintainability. Improved readability makes it easier to maintain. Buying a change reduces risk. Change is inevitable, and if the single responsibility principle is followed well, when you modify one function, you can significantly reduce the impact on other functions.
The single responsibility principle is the simplest but most difficult principle to use. It requires designers to discover different responsibilities of a class, separate them, and encapsulate them into different classes or modules. The multiple responsibilities of discovery classes require designers to have strong analytical design ability and relevant refactoring experience. The following describes the application of the single responsibility principle by taking the university student work management procedure as an example.
Procedures for the management of university student work. Analysis: the university student work mainly includes two aspects of student life guidance and students’ academic guidance work, including counselling mainly includes the construction of class committee, attendance statistics, psychological counseling, cost reminder, class management, academic instruction mainly includes professional guidance, counselling, scientific guidance, learning summary, etc. It is obviously unreasonable to assign these tasks to a teacher. The correct way to do this is to assign life guidance to counselors and academic guidance to academic mentors, as shown in Figure 1.
FIG. 6 Class diagram of university student work management program
Note: Single responsibility applies to methods as well. A method should do one thing as well as possible. If a method does too many things, its granularity becomes too coarse for reuse.
1.3.5 Interface Segregation Principle
The Interface Segregation Principle (ISP) requires programmers to break bloated interfaces into smaller, more specific interfaces, so that the interfaces contain only the methods that the customer is interested in.
In 2002, Robert C. Martin defined the “interface isolation principle” as: Clients should not be forced to depend on methods they do not use. There is another definition of this principle: The dependency of one class to another one should depend on The smallest possible interface. What these two definitions mean is that instead of trying to create a huge interface for all the classes that depend on it, create a special interface for each class.
The interface isolation principle and the single responsibility principle, both intended to improve the cohesion of classes and reduce coupling between them, embody the idea of encapsulation, but they are different:
The principle of single-responsibility pays attention to responsibility, while interface isolation pays attention to isolating interface dependencies. The principle of single-responsibility, which governs classes, targets implementation and details in a program; The interface isolation principle mainly constrains interfaces and is aimed at abstraction and the construction of the overall framework of the program.
[Note] The single responsibility principle and the interface isolation principle are also referred to as the high cohesion and low coupling principle.
Advantages of interface isolation The interface isolation principle is used to constrain interfaces and reduce the dependency of classes on interfaces. It has the following advantages.
Buying a system by splitting a bloated interface into smaller-grained ones prevents the proliferation of external changes and increases the flexibility and maintainability of the system. Interface isolation boosts system cohesion, reduces external interactions, and reduces system coupling. Automatically defining the granularity of interfaces ensures system stability. However, if the definition is too small, it will lead to too many interfaces and complicate the design. If the definition is too large, flexibility is reduced and customization services cannot be provided, bringing unpredictable risks to the overall project. Using multiple dedicated interfaces also lends itself to hierarchies of objects, as the overall interface can be defined through interface inheritance. Buy a ticket to reduce code redundancy on a project. A large interface that is too large often places a lot of unnecessary methods inside it, forcing redundant code to be designed when implementing the interface.
The implementation of the interface isolation principle Is based on the following rules.
The interface to buy a ticket is small, but limited. An interface serves only one submodule or business logic. Queues customize services for classes that rely on interfaces. Provide only the methods needed by the caller and mask those that are not. Learn your environment by buying something to do, instead of blindly following your ideas. Each project or product has selected environmental factors, and depending on the environment, the criteria for interface separation will vary to gain insight into the business logic. Buy a way to promote cohesion and reduce external interactions. Make the interface do the most with the fewest methods.
The following uses the student score management program as an example to describe the application of the interface isolation principle.
The program of student achievement management.
Analysis: Student grade management procedures generally include insert, delete, modify the results, calculate the total score, divide calculation, printing performance information, query results, and other functions, if all these features into an interface obviously is not very reasonable, right, it is respectively in the input module, statistics module and print module three modules, Its class diagram is shown in Figure 7.
Figure 7 Class diagram of student score management program
The program code is as follows:
package principle;
public class ISPtest
{
public static void main(String[] args)
{
InputModule input =StuScoreList.getInputModule();
CountModule count =StuScoreList.getCountModule();
PrintModule print =StuScoreList.getPrintModule();
input.insert();
count.countTotalScore();
print.printStuInfo();
//print.delete();}}// Input module interface
interface InputModule
{
void insert(a);
void delete(a);
void modify(a);
}
// Statistics module interface
interface CountModule
{
void countTotalScore(a);
void countAverage(a);
}
// Print module interface
interface PrintModule
{
void printStuInfo(a);
void queryStuInfo(a);
}
/ / implementation class
class StuScoreList implements InputModule.CountModule.PrintModule
{
private StuScoreList(a){}
public static InputModule getInputModule(a)
{
return (InputModule)new StuScoreList();
}
public static CountModule getCountModule(a)
{
return (CountModule)new StuScoreList();
}
public static PrintModule getPrintModule(a)
{
return (PrintModule)new StuScoreList();
}
public void insert(a)
{
System.out.println("Insert () method of input module called!");
}
public void delete(a)
{
System.out.println("Input module's delete() method called!");
}
public void modify(a)
{
System.out.println("Input module's modify() method is called!");
}
public void countTotalScore(a)
{
System.out.println(The countTotalScore() method of the statistics module is called!);
}
public void countAverage(a)
{
System.out.println("The countAverage() method of the statistics module is called!");
}
public void printStuInfo(a)
{
System.out.println("The printStuInfo() method of the print module is called!");
}
public void queryStuInfo(a)
{
System.out.println("Print module's queryStuInfo() method called!"); }}Copy the code
The running results of the program are as follows:
The insert() method of the input module is called! The countTotalScore() method of the statistics module is called! The printStuInfo() method of the print module is called!Copy the code
1.3.6 Demeter Principle, also known as Demeter Principle
The Law of Demeter (LoD) is also known as the Least Knowledge Principle (LKP), Born in 1987 as a Northeastern University research project called Demeter, proposed by Ian Holland and popularized by Booch, one of the founders of UML, He later became known as The Pragmatic Programmer in his classic book. Demeter’s Law is defined as: Talk only to your immediate friends and not to strangers. The implication is that if two software entities do not communicate directly, then direct calls to each other should not occur and can be forwarded by a third party. Its purpose is to reduce the degree of coupling between classes and improve the relative independence of modules.
The “friend” in Demeter’s law refers to the current object itself, its member object, the object created by the current object, the method parameters of the current object, etc. These objects are associated, aggregated or combined with the current object, and can directly access the methods of these objects.
Advantages of Demeter’s Rule Demeter’s rule requires that the width and depth of communication between software entities be limited. Using Demeter’s rule correctly will have the following two advantages.
Unconsciously reduces the coupling between classes and increases the relative independence of modules. Lent increases class reusability and system scalability due to reduced affinity.
However, the excessive use of Demeter’s rule will make the system produce a large number of mediation classes, which will increase the complexity of the system and reduce the communication efficiency between modules. Therefore, the application of Demeter’s rule requires repeated trade-offs to ensure high cohesion and low coupling as well as clear system structure.
Demeter’s rule from the definition and characteristics of Demeter’s rule, it emphasizes the following two points:
Buying a way to rely on something, from a dependent’s point of view, only relies on what you’re supposed to rely on. According to the dependent, they only expose the method they’re supposed to expose, anyway.
So here are six things to keep in mind when applying Demeter’s rule.
Buy a way to partition classes by buying something, you create classes that are weakly coupled. The weaker the coupling between classes, the better the goal of reuse. A class is structured to minimize access rights for its members. Anyway, class design prioritizes making a class immutable. Queues minimize the number of references to other objects in a way that references to other classes. Instead of exposing the attribute members of a class, the corresponding accessors (set and GET methods) are provided. Automatically uses Serializable functionality discreetly.
An example of the relationship between a star and his agent. Analysis: Celebrities devote themselves to their art, so many daily affairs are handled by their agents, such as meeting with fans and negotiating with media companies. Here agents are friends of the stars, and fans and media companies are strangers, so Demeter’s rule is appropriate, as shown in the diagram below.
FIG. 8 The relationship between stars and agents
The program code is as follows:
package principle;
public class LoDtest
{
public static void main(String[] args)
{
Agent agent=new Agent();
agent.setStar(new Star("Ruby"));
agent.setFans(new Fans("Fan Han Seung"));
agent.setCompany(new Company("China Media Limited")); agent.meeting(); agent.business(); }}/ / agent
class Agent
{
private Star myStar;
private Fans myFans;
private Company myCompany;
public void setStar(Star myStar)
{
this.myStar=myStar;
}
public void setFans(Fans myFans)
{
this.myFans=myFans;
}
public void setCompany(Company myCompany)
{
this.myCompany=myCompany;
}
public void meeting(a)
{
System.out.println(myFans.getName()+"With the Stars"+myStar.getName()+"见面了。");
}
public void business(a)
{
System.out.println(myCompany.getName()+"With the Stars"+myStar.getName()+"Negotiate business."); }}/ / star
class Star
{
private String name;
Star(String name)
{
this.name=name;
}
public String getName(a)
{
returnname; }}/ / fans
class Fans
{
private String name;
Fans(String name)
{
this.name=name;
}
public String getName(a)
{
returnname; }}// Media companies
class Company
{
private String name;
Company(String name)
{
this.name=name;
}
public String getName(a)
{
returnname; }}Copy the code
The running results of the program are as follows:
Fans Han Cheng and star Ruby Lin met. China Media corp. talks business with star Ruby Lin.Copy the code
1.3.7 Composite Reuse Principle
Composite Reuse Principle (CRP) is also called Composition/Aggregate Reuse Principle (CARP). It requires that in software reuse, we should first use association relation such as combination or aggregation, and then consider using inheritance relation. If inheritance is to be used, the Richter substitution principle must be strictly followed. The principle of composite reuse and the Principle of Richter’s substitution complement each other.
The importance of the principle of composite reuse Usually class reuse is divided into inheritance reuse and composite reuse. Although inheritance reuse has the advantages of simplicity and easy implementation, it also has the following disadvantages.
Inherit reuse breaks the encapsulation of a class. Because inheritance exposes the implementation details of the parent class to the child class, the parent class is transparent to the child class, so this reuse is also known as “white box” reuse. Unconsciously, a subclass has a high coupling degree with its parent class. Any change in the implementation of the parent class results in a change in the implementation of the subclass, which is not conducive to the extension and maintenance of the class. Unconsciously it limits the flexibility of reuse. An implementation inherited from a parent class is static and defined at compile time, so it cannot be changed at run time.
When using composite or aggregate reuse, existing objects can be incorporated into the new object, making it a part of the new object. The new object can invoke the functions of the existing object, which has the following advantages.
Unconsciously, it maintains the encapsulation of a class. Because the internal details of component objects are invisible to the new object, this reuse is also known as “black box” reuse. Unconsciously there is low coupling between old and new classes. This reuse requires fewer dependencies, and the only way a new object can access a component object is through its interface. Lent reuse is flexible. This reuse can occur dynamically at run time, with new objects dynamically referencing objects of the same type as component objects.
The principle of composite reuse is implemented by incorporating existing objects into new objects as member objects of new objects. The new objects can call the functions of existing objects to achieve reuse.
The application of the principle of composite multiplexing is illustrated by the example of the vehicle classification management program.
The automobile classification management program. Analysis: according to the “power source” can be divided into gasoline vehicles, electric vehicles, etc. According to the “color” can be divided into white cars, black cars and red cars. If you consider both categories, the combinations are numerous. Figure 9 shows a class diagram of vehicle classification implemented with the stepnet: relation.
FIG. 9 Class diagram of automobile classification with inheritance relation
As you can see from Figure 9, the implementation of inheritance generates many subclasses, and adding a new “power source” or a new “color” requires modifying the source code, which violates the open closed principle and is clearly not desirable. However, the above problems can be well solved by using combinatorial relations, as shown in the figure for its class diagram.
FIG. 10 Class diagram of automobile classification realized by combinatorial relation
Combined with the content of the previous sections, we introduce seven design principles, which are open closed principle, Richter’s substitution principle, dependency inversion principle, single responsibility principle, interface isolation principle, Demeter’s rule and the composite reuse principle introduced in this section.
These seven design principles are the principles that software design patterns must follow, and they require different emphases. Among them, the open and closed principle is the general principle, it tells us to be open to expansion, to modify closed; Richter’s substitution tells us not to break the inheritance system; The dependency inversion principle tells us to program for interfaces; The single responsibility principle tells us to implement a class with a single responsibility; The principle of interface isolation tells us to keep interfaces simple and simple when designing them. Demeter’s rule tells us to reduce coupling; The principle of composite reuse tells us to use composite or aggregate relation reuse in preference to inheritance relation reuse.