Describes how to combine classes or objects to form a larger structure
Structural patterns can be divided into class structural patterns and object structural patterns:
Class structured pattern: generally, there are only inheritance and implementation relationships.
Object structured patterns: according to the "composite Reuse Principle", try to use association relationships to replace inheritance relationships in the system. Therefore, most structural patterns are object structured patterns.
1. Adapter mode
It is mainly divided into three categories: class adapter mode, object adapter mode and interface adapter mode
By converting one interface to another that the customer wants, the adapter pattern allows classes that are incompatible with the interface to work together, alias wrappers
working principle
- Adapter mode: convert the interface of one class to another. Make the classes that are incompatible with the original interface compatible
- From the user's point of view, the Adaptee is decoupled
- The user calls the target interface method transformed by the adapter, and the adapter then calls the relevant interface method of the Adaptee
- After receiving the feedback result, the user feels that he is only interacting with the target interface
Model motivation
The client can access the services provided by the target class through its interface. Sometimes, the existing class can meet the functional needs of the customer class, but the interface it provides is not necessarily expected by the customer class. The existing interface needs to be transformed into the interface expected by the customer class.
When the client class calls the method of the adapter, the method of the adapter class will be called inside the adapter class, and this process is transparent to the client class. The client class does not directly access the adapter class. Therefore, the adapter enables classes that cannot interact due to interface incompatibility to work together.
Pattern structure
- Class Adapter
- object adapter
Target: the target abstract class defines the interface required by the customer. It can be an abstract class or interface or a concrete class
Adapter: the adapter class can call another interface as a converter to adapt the Adaptee and Target. The adapter class is the core of the adapter pattern. In the object adapter, it connects the two by inheriting the Target and associating an Adaptee object
Adaptee: the adapter class is the role to be adapted. It defines an existing interface that needs to be adapted. The adapter class is generally a specific class that contains the business methods that customers want to use. In some cases, there may be no source code of the adapter class
Client: customer class
Usage example of class adapter pattern
demand
Take the example of charger in life to explain the Adapter. The charger itself is equivalent to the Adapter, 220V AC is equivalent to src (i.e. the Adapter), and our target DST (i.e. the target) is 5V DC
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === Class adapter mode ===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter()); } }
//Adapter interface public interface IVoltage5V { public int output5V(); }
//Adapted class public class Voltage220V { //Output 220V voltage public int output220V() { int src = 220; System.out.println("Voltage=" + src + "Crouch"); return src; } }
//adapter class public class VoltageAdapter extends Voltage220V implements IVoltage5V { @Override public int output5V() { // TODO Auto-generated method stub //220V voltage obtained int srcV = output220V(); int dstV = srcV / 44 ; //Convert to 5v return dstV; } }
public class Phone { //charge public void charging(IVoltage5V iVoltage5V) { if(iVoltage5V.output5V() == 5) { System.out.println("The voltage is 5 V, Can charge~~"); } else if (iVoltage5V.output5V() > 5) { System.out.println("Voltage greater than 5 V, Can't charge~~"); } } }
Examples of using the object adapter pattern
demand
Take the example of charger in life to explain the Adapter. The charger itself is equivalent to the Adapter, 220V AC is equivalent to src (i.e. the Adaptee), and our target DST (i.e. the target) is 5V DC, which is completed by using the object Adapter mode.
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === Object Adapter Pattern ===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter(new Voltage220V())); } }
//Adapter interface public interface IVoltage5V { public int output5V(); }
//Adapted class public class Voltage220V { //Output 220V voltage, unchanged public int output220V() { int src = 220; System.out.println("Voltage=" + src + "Crouch"); return src; } }
//adapter class public class VoltageAdapter implements IVoltage5V { private Voltage220V voltage220V; // Association - aggregation //Pass in a Voltage220V instance through the constructor public VoltageAdapter(Voltage220V voltage220v) { this.voltage220V = voltage220v; } @Override public int output5V() { int dst = 0; if(null != voltage220V) { int src = voltage220V.output220V();//Obtain 220V voltage System.out.println("Use the object adapter for adaptation~~"); dst = src / 44; System.out.println("After adaptation, the output voltage is=" + dst); } return dst; } }
public class Phone { //charge public void charging(IVoltage5V iVoltage5V) { if(iVoltage5V.output5V() == 5) { System.out.println("The voltage is 5 V, Can charge~~"); } else if (iVoltage5V.output5V() > 5) { System.out.println("Voltage greater than 5 V, Can't charge~~"); } } }
Object adapter pattern considerations and details
- Object adapter and class adapter are actually the same idea, but they are implemented in different ways.
According to the principle of composite reuse, composition is used to replace inheritance, so it solves the limitation that class adapters must inherit src, and it no longer requires dst to be an interface. - Lower cost and more flexible.
Use example of interface adapter mode
demand
- The attribute animation ValueAnimator class in Android can add listeners through the addListener(AnimatorListener listener) method. The general writing method is as follows:
- Sometimes we don't want to implement all the methods of the Animator.AnimatorListener interface,
We just want to monitor onAnimationStart. We will write as follows
public interface Interface4 { public void m1(); public void m2(); public void m3(); public void m4(); }
//In AbsAdapter, we implement the method of Interface4 by default public abstract class AbsAdapter implements Interface4 { //Default implementation public void m1() { } public void m2() { } public void m3() { } public void m4() { } }
public class Client { public static void main(String[] args) { AbsAdapter absAdapter = new AbsAdapter() { //We just need to override. We need to use interface methods @Override public void m1() { // TODO Auto-generated method stub System.out.println("Used m1 Method of"); } }; absAdapter.m1(); } }
Source code analysis of the application of adapter pattern in spring MVC framework
pattern analysis
Class adapter mode
advantage:
- Decouple the target class from the adapter class
- It increases the transparency and reusability of classes
- Flexibility and scalability are very good, in line with the "opening and closing principle".
Disadvantages:
- For Java, C# and other languages that do not support multiple inheritance, at most one adapter class can be adapted at a time, and the target abstract class can only be an abstract class, not a concrete class. Its use has certain limitations, and an adapter class and its subclasses cannot be adapted to the target interface.
Class adapter pattern considerations and details
- Java is a single inheritance mechanism, so the class adapter needs to inherit src class, which is a disadvantage, because it requires dst to be an interface, which has certain limitations;
- The methods of src class will be exposed in the Adapter, which also increases the cost of use.
- Because it inherits the src class, it can rewrite the methods of the src class according to requirements, which enhances the flexibility of the Adapter.
Object Adapter Pattern
advantage:
- The same adapter can adapt the adapter class and its subclasses to the target interface.
Disadvantages:
- It is not easy to replace the methods of the adapter class. If you must replace one or more methods of the adapter class, you have to first make a subclass of the adapter class, replace the methods of the adapter class, and then adapt the subclass of the adapter class as a real adapter. The implementation process is more complex.
Considerations and details of adapter mode
- The three naming methods are named according to the form in which src is given to the Adapter (the form in the Adapter).
- Class Adapter: given by class, in the Adapter, src is regarded as a class and inherited
Object Adapter: the object is given. In the Adapter, src is taken as an object and held
Interface Adapter: the interface is given to. In the Adapter, src is used as an interface to implement - The biggest function of the Adapter pattern is to integrate incompatible interfaces.
- In the actual development, the implementation is not limited to the three classic forms we explain
2. Bridging mode
- Bridge pattern refers to putting the implementation and abstraction in two different class levels, so that the two levels can be changed independently.
- It is a structural design mode
- Bridge pattern is based on the minimum design principle of classes. Different classes assume different responsibilities by using encapsulation, aggregation and inheritance. Its main feature is the realization of abstraction and behavior
(Implementation) is separated, so that the independence of each part can be maintained and their function expansion can be handled
Model motivation
When different change dimensions (i.e. different change reasons) are integrated together, no matter which dimension is expanded, it is bound to affect another dimension. There is strong coupling between dimensions.
Pattern structure
The bridging mode contains the following roles
Applicable examples of bridge connection mode
demand
Now you can implement operation programming for different brands of different mobile phone types (for example:
Power on, power off, surfing the Internet, making phone calls, etc.),
As shown in the figure:
tradition
Analysis of traditional solutions to mobile phone operation problems
- Scalability problem (class explosion). If we add more mobile phone styles (rotary), we need to add mobile phone classes of various brands. Similarly, if we add a mobile phone brand, we also need to add them under each mobile phone style class.
- In violation of the principle of single responsibility, when we add mobile phone styles, we should add all brands of mobile phones at the same time, which increases the cost of code maintenance
- Solution - use bridge mode
improvement
Using bridge mode to improve the traditional way, make the program have good expansibility, and use program maintenance
public class Client { public static void main(String[] args) { //Get a folding phone (style + brand) Phone phone1 = new FoldedPhone(new XiaoMi()); phone1.open(); phone1.call(); phone1.close(); System.out.println("======================="); Phone phone2 = new FoldedPhone(new Vivo()); phone2.open(); phone2.call(); phone2.close(); System.out.println("=============="); UpRightPhone phone3 = new UpRightPhone(new XiaoMi()); phone3.open(); phone3.call(); phone3.close(); System.out.println("=============="); UpRightPhone phone4 = new UpRightPhone(new Vivo()); phone4.open(); phone4.call(); phone4.close(); } }
public abstract class Phone { //Combined brand private Brand brand; //constructor public Phone(Brand brand) { super(); this.brand = brand; } protected void open() { this.brand.open(); } protected void close() { brand.close(); } protected void call() { brand.call(); } }
//Foldable mobile Phone class, inheriting abstract class Phone public class FoldedPhone extends Phone { //constructor public FoldedPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" Folding style phone "); } public void close() { super.close(); System.out.println(" Folding style phone "); } public void call() { super.call(); System.out.println(" Folding style phone "); } }
public class UpRightPhone extends Phone { //constructor public UpRightPhone(Brand brand) { super(brand); } public void open() { super.open(); System.out.println(" Upright style mobile phone "); } public void close() { super.close(); System.out.println(" Upright style mobile phone "); } public void call() { super.call(); System.out.println(" Upright style mobile phone "); } }
public class Vivo implements Brand { @Override public void open() { // TODO Auto-generated method stub System.out.println(" Vivo Mobile phone on "); } @Override public void close() { // TODO Auto-generated method stub System.out.println(" Vivo Mobile phone off "); } @Override public void call() { // TODO Auto-generated method stub System.out.println(" Vivo Phone call "); } }
public class XiaoMi implements Brand { @Override public void open() { // TODO Auto-generated method stub System.out.println(" Millet phone on "); } @Override public void close() { // TODO Auto-generated method stub System.out.println(" Xiaomi mobile phone off "); } @Override public void call() { // TODO Auto-generated method stub System.out.println(" Millet phone call "); } }
Source code analysis of bridge mode in JDBC
- The Driver interface of the Jdbc. From the bridge mode, the Driver is an interface, which can be found below
MySQL Driver and Oracle Driver can be used as implementation interface classes - Code analysis + Debug source code
pattern analysis
advantage:
- Instead of multiple / multi-layer inheritance, the multiple / multi-layer inheritance scheme violates the "single responsibility principle", has poor reusability, and the bridging mode greatly reduces the number of subclasses
- The scalability of the system is improved. If one of the two change dimensions is extended arbitrarily, there is no need to modify the original system, which conforms to the "opening and closing principle".
Disadvantages:
- The abstraction layer increases the difficulty of system understanding and design.
- It is required to correctly identify two independent changing dimensions in the system, so its application scope has certain limitations, and the identification also needs experience accumulation.
Precautions and details of bridging mode
- It realizes the separation of abstract part and implementation part, which greatly provides the flexibility of the system and makes the abstract part and implementation part independent, which is helpful for the hierarchical design of the system, so as to produce a better structured system.
- For the high-level part of the system, you only need to know the interface between the abstract part and the implementation part, and the other parts are completed by the specific business.
- Bridging mode replaces multi-layer inheritance scheme, which can reduce the number of subclasses and reduce the management and maintenance cost of the system.
- The introduction of bridging mode increases the difficulty of system understanding and design. Because the aggregation association relationship is based on the abstraction layer, developers are required to design and program for the abstraction
- The bridging mode requires to correctly identify two independently changing dimensions in the system, so its scope of use has certain limitations, that is, it needs such an application scenario
Mode applicable environment
Bridge mode can be used when:
- A class has two (or more) dimensions that change independently, and both (or more) dimensions need to be extended independently.
- The bridging mode is especially suitable for those systems that do not want to use inheritance or the number of system classes increases sharply due to multiple / multi-layer inheritance.
When using the bridge pattern, we should first identify the two independently changing dimensions of a class, design them as two independent inheritance hierarchical structures, provide an abstract layer for both dimensions, and establish Abstract coupling.
Generally, we design some common business methods of classes with two independent change dimensions and the dimension most closely related to them as the "abstract class" hierarchy (abstract part), and the other dimension as the "implementation class" hierarchy (implementation part).
Bridge mode and other application scenarios
- Bridging mode is especially suitable for those systems that do not want to use inheritance or the number of system classes increases sharply due to multi-level inheritance
- Common application scenarios:
-JDBC Driver
-Bank transfer system
Transfer classification: online transfer, counter transfer, AMT transfer
Transfer user type: ordinary user, silver card user, gold card user
-Message management
Message type: instant message, delayed message
Message classification: SMS, email, QQ
3. Combination mode
It creates a tree structure of object groups and combines objects into a tree structure to represent the hierarchical relationship of "whole part".
The combination mode combines objects according to the tree structure, which is used to represent the partial and overall levels.
The combination mode makes the user's access to single objects and combined objects consistent, that is, combination allows customers to deal with individual objects and combined objects in a consistent way
Model motivation
For a tree structure, when a method of a container object (such as a folder) is called, it will traverse the whole tree structure, find the member object (which can be a container object or a leaf object, such as subfolders and files) that also contains this method, and call it for execution. (recursive call)
Due to the functional differences between container objects and leaf objects, container objects and leaf objects must be treated differently in the client code using these objects. In fact, we want to deal with them consistently in most cases, because the different treatment of these objects will make the program very complex.
Pattern structure
Use examples of composite mode
demand
Write a program to display the department structure of a school: the demand is that the Department composition of the school should be displayed on one page. A school has multiple colleges and a college has multiple departments.
tradition
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //Creating object schools from large to small OrganizationComponent university = new University("Tsinghua University", " Top universities in China "); //Create College OrganizationComponent computerCollege = new College("school of computing", " school of computing "); OrganizationComponent infoEngineercollege = new College("School of Information Engineering", " School of Information Engineering "); //Create departments (majors) under each college computerCollege.add(new Department("software engineering", " Software engineering is good ")); computerCollege.add(new Department("Network engineering", " Network engineering is good ")); computerCollege.add(new Department("Computer science and technology", " Computer science and technology is an old specialty ")); // infoEngineercollege.add(new Department("communication engineering", " Communication engineering is not easy to learn ")); infoEngineercollege.add(new Department("Information Engineering", " Information engineering is easy to learn ")); //Add college to school university.add(computerCollege); university.add(infoEngineercollege); //university.print(); infoEngineercollege.print(); } }
public class College extends OrganizationComponent { //Department stored in List List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>(); // constructor public College(String name, String des) { super(name, des); // TODO Auto-generated constructor stub } // Override add @Override protected void add(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub // In the actual business in the future, collage add and University add may not be exactly the same organizationComponents.add(organizationComponent); } // Override remove @Override protected void remove(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.remove(organizationComponent); } @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getDes() { // TODO Auto-generated method stub return super.getDes(); } // The print method is to output the colleges included in the University @Override protected void print() { // TODO Auto-generated method stub System.out.println("--------------" + getName() + "--------------"); //Traverse organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }
public class Department extends OrganizationComponent { //No collection public Department(String name, String des) { super(name, des); // TODO Auto-generated constructor stub } //Add and remove do not need to be written because they are leaf nodes @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getDes() { // TODO Auto-generated method stub return super.getDes(); } @Override protected void print() { // TODO Auto-generated method stub System.out.println(getName()); } }
public abstract class OrganizationComponent { private String name; // name private String des; // explain protected void add(OrganizationComponent organizationComponent) { //Default implementation throw new UnsupportedOperationException(); } protected void remove(OrganizationComponent organizationComponent) { //Default implementation throw new UnsupportedOperationException(); } //constructor public OrganizationComponent(String name, String des) { super(); this.name = name; this.des = des; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } //The print method is made abstract, and subclasses need to be implemented protected abstract void print(); }
//University is a Composite, which can manage colleges public class University extends OrganizationComponent { List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>(); // constructor public University(String name, String des) { super(name, des); // TODO Auto-generated constructor stub } // Override add @Override protected void add(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.add(organizationComponent); } // Override remove @Override protected void remove(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.remove(organizationComponent); } @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getDes() { // TODO Auto-generated method stub return super.getDes(); } // The print method is to output the colleges included in the University @Override protected void print() { // TODO Auto-generated method stub System.out.println("--------------" + getName() + "--------------"); //Traverse organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }
Source code analysis of composite pattern in JDK set
- The Java collection class HashMap uses the composite pattern
- Code analysis + Debug source code
pattern analysis
advantage:
- Composite mode can clearly define hierarchical complex objects and facilitate the control of the whole hierarchy.
- The client can consistently use a composite structure or a single object in it, simplifying the client code.
- It is convenient to add containers and leaf components, which conforms to the "opening and closing principle".
- Through the recursive combination of leaf object and container object, a complex tree structure can be formed, but the control of tree structure is very simple.
Disadvantages:
- When adding new components, it is difficult to limit the type of components in the container.
The key of the composite pattern is to define an abstract component class, which can represent both leaves and containers. The client can program the abstract component class without knowing whether it represents leaves or containers.
At the same time, an aggregation association relationship is established between the container object and the abstract component class. The container object can contain both leaves and containers, so as to realize recursive combination and form a tree structure.
If the composite mode is not used, the client code will rely too much on the complex internal implementation structure of the container object. The change of the internal implementation structure of the container object will cause frequent changes of the client code, resulting in the disadvantages of complex code maintenance and poor scalability. The introduction of combination mode will solve these problems to a certain extent.
Notes and details of combination mode
- Simplify client operations. The client only needs to face consistent objects without considering the whole part or node leaf.
- It has strong scalability. When we want to change the composite object, we only need to adjust the internal hierarchical relationship, and the client does not need to make any changes
- Facilitate the creation of complex hierarchies. The client does not care about the composition details in the composition, and it is easy to add nodes or leaves to create a complex tree structure
- When you need to traverse the organization, or the object you are dealing with has a tree structure, it is very suitable to use the composite mode
- It requires high abstraction. If there are many differences between nodes and leaves, for example, many methods and attributes are different, it is not suitable to use composite mode
Mode applicable environment
Combination mode can be used in the following cases:
- In a hierarchy with whole and part, we want to ignore the differences between the whole and part in a way, and the client can treat them consistently.
- In a system developed with object-oriented language, we need to deal with a tree structure.
- Leaf objects and container objects can be separated in a system, and their types are not fixed, so some new types need to be added.
4. Decoration mode
Dynamically attach new functions to objects. In terms of object function extension, it is more important than inheritance
It is flexible, and the decorator mode also embodies the principle of opening and closing (ocp)
Model motivation
The decorator uses the association relationship between objects to replace the inheritance relationship between classes. In the decorator, you can not only call the methods of the original class, but also add new methods to expand the functions of the original class. It dynamically adds responsibilities to objects without defining subclasses, which is in line with the principle of composition and reuse.
Pattern structure
- Decorator mode is like packing a courier
Main body: for example: ceramics, clothes (Component) / / decorated person
Packaging: for example: newspaper stuffing, plastic foam, cardboard, board (Decorator) - Component
Main body: for example, similar to the previous Drink - ConcreteComponent and Decorator
ConcreteComponent: concrete body,
For example, the individual coffee items in front
Decorator: decorator, such as spices - Between Component and ConcreteComponent as shown in the figure, if
There are many ConcreteComponent classes. You can also design a buffer layer to extract the common parts, and the abstraction layer is a class.
Use examples of decoration mode
demand
- Coffee type / single product: Espresso, ShortBlack, longblack, decaf
- Seasoning: Milk, soy Milk, Chocolate
- It is required to have good expansibility, easy modification and maintenance when expanding new coffee varieties
- Use OO to calculate the cost of different kinds of coffee: customers can order single coffee or single coffee + seasoning combination.
tradition
Scheme I
- Drink is an abstract class that represents a drink
- des is a description of coffee, such as the name of coffee
- The cost() method is to calculate the cost and make an abstract method in the sink class
- Decaf is a single coffee. It inherits Drink and realizes cost
- Espresso & & milk is a single coffee + seasoning. There are many combinations
- Problem: with this design, there will be many classes. When we add a single coffee or a new seasoning, the number of classes will double and there will be a class explosion
Scheme II
According to the previous analysis, in scheme 1, because the combination of coffee single product and seasoning will double the number of classes, improvements can be made to build seasonings into the Drink class, so as not to cause too many classes. So as to improve the quality of the project
Maintainability of (as shown in the figure)
Problem analysis of scheme 2 -
- Scheme 2 can control the number of classes without causing many classes
- When adding or deleting a seasoning type, the maintenance of the code is very large
- Considering that you can add multiple condiments, you can return hasMilk to a corresponding int
- Consider using decorator mode
improvement
//Specific Decorator, here is the seasoning public class Chocolate extends Decorator { public Chocolate(Drink obj) { super(obj); setDes(" Chocolates "); setPrice(3.0f); // Price of condiment } }
public class Coffee extends Drink { @Override public float cost() { // TODO Auto-generated method stub return super.getPrice(); } }
public class DeCaf extends Coffee { public DeCaf() { setDes(" Coffee without cause "); setPrice(1.0f); } }
public class Decorator extends Drink { private Drink obj; public Decorator(Drink obj) { //combination // TODO Auto-generated constructor stub this.obj = obj; } @Override public float cost() { // TODO Auto-generated method stub // getPrice own price return super.getPrice() + obj.cost(); } @Override public String getDes() { // TODO Auto-generated method stub // obj.getDes() outputs the information of the decorated person return des + " " + getPrice() + " && " + obj.getDes(); } }
public abstract class Drink { public String des; // describe private float price = 0.0f; public String getDes() { return des; } public void setDes(String des) { this.des = des; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } //Abstract method of calculating cost //Subclasses public abstract float cost(); }
public class Espresso extends Coffee { public Espresso() { setDes(" Italian Coffee "); setPrice(6.0f); } }
public class LongBlack extends Coffee { public LongBlack() { setDes(" longblack "); setPrice(5.0f); } }
public class Milk extends Decorator { public Milk(Drink obj) { super(obj); // TODO Auto-generated constructor stub setDes(" milk "); setPrice(2.0f); } }
public class ShortBlack extends Coffee{ public ShortBlack() { setDes(" shortblack "); setPrice(4.0f); } }
public class Soy extends Decorator{ public Soy(Drink obj) { super(obj); // TODO Auto-generated constructor stub setDes(" Soybean Milk "); setPrice(1.5f); } }
public class CoffeeBar { public static void main(String[] args) { // TODO Auto-generated method stub // Order in decorator mode: 2 chocolate + 1 milk LongBlack // 1. Order a LongBlack Drink order = new LongBlack(); System.out.println("Cost 1=" + order.cost()); System.out.println("describe=" + order.getDes()); // 2. order add a portion of milk order = new Milk(order); System.out.println("order Add a milk charge =" + order.cost()); System.out.println("order Add a milk description = " + order.getDes()); // 3. order add a chocolate order = new Chocolate(order); System.out.println("order Add a portion of milk and a portion of chocolate =" + order.cost()); System.out.println("order Add a portion of milk and a portion of chocolate = " + order.getDes()); // 3. order add a chocolate order = new Chocolate(order); System.out.println("order Add 1 milk and 2 chocolate =" + order.cost()); System.out.println("order Add 1 part milk and 2 parts chocolate = " + order.getDes()); System.out.println("==========================="); Drink order2 = new DeCaf(); System.out.println("order2 No coffee charge =" + order2.cost()); System.out.println("order2 Coffee description = " + order2.getDes()); order2 = new Milk(order2); System.out.println("order2 No charge for adding a portion of milk to coffee =" + order2.cost()); System.out.println("order2 Add a milk description to the coffee = " + order2.getDes()); } }
Source code analysis of decorator mode in JDK application
The IO structure of Java, FilterInputStream, is a decorator
public abstract class InputStream implements Closeable {} / / is an abstract class, i.e. Component
Public class filterinputstream extensions InputStream {/ / is a Decorator class Decorator
protected volatile InputStream in / / decorated object}
Class datainputstream extensions filterinputstream implementations datainput {/ / filterinputstream subclass,
pattern analysis
advantage:
- For extending the function of an object, decoration mode is more flexible than inheritance and will not lead to a sharp increase in the number of classes.
- The function of an object can be extended in a dynamic way. Different specific decoration classes can be selected at run time through the configuration file, so as to realize different behaviors.
- You can decorate an object many times. By using different specific decoration classes and the arrangement and combination of these decoration classes, you can create many combinations of different behaviors and get more powerful objects.
- Specific component classes and specific decoration classes can be changed independently. Users can add new specific component classes and specific decoration classes as needed. The original class library code does not need to be changed and conforms to the "opening and closing principle".
Disadvantages:
- When using decoration mode for system design, many small objects will be generated. The difference between these objects is that they are connected in different ways, rather than their class or attribute values. The generation of a large number of small objects is bound to occupy more system resources and affect the performance of a certain program.
- Decoration mode provides a more flexible solution than inheritance, but it also means that it is more prone to errors than inheritance, and troubleshooting is also very difficult. For objects decorated for many times, it may be cumbersome to find errors level by level during debugging.
Because both concrete component classes and decoration classes implement the same abstract component interface, the client will not feel that the object is different before and after decoration. Decoration mode can expand the function of the object without creating more subclasses
Mode applicable environment
Add responsibilities to a single object in a dynamic and transparent manner without affecting other objects.
When the system cannot be extended by inheritance or inheritance is not conducive to system expansion and maintenance, decoration mode can be used. There are two main types of situations where inheritance cannot be used: the first is that there are a large number of independent extensions in the system. In order to support each extension or combination of extensions, a large number of subclasses will be generated, resulting in an explosive increase in the number of subclasses; the second is that Class is because the class is defined as not inheritable
5. Appearance mode
Appearance mode provides a consistent interface for a group of interfaces in the subsystem. This mode defines a high-level interface, which makes the subsystem easier to use
The appearance mode defines a consistent interface to shield the details of the internal subsystem, so that the caller only needs to call the interface without paying attention to the internal details of the subsystem
Model motivation
When appearance roles are introduced, users only need to interact directly with appearance roles, and the complex relationship between users and subsystems is realized by appearance roles, which reduces the coupling degree of the system.
Pattern structure
- Facade: provides a unified calling interface for the caller. The facade knows which subsystems are responsible for processing requests, so as to proxy the caller's requests to appropriate subsystem objects
- Caller (Client): the caller of the appearance interface
- Subsystem collection: refers to the module or subsystem that handles the tasks assigned by the Facade object. It is the actual provider of functions
Usage examples of appearance mode
demand
Set up a home theater:
DVD player, projector, automatic screen, surround sound and popcorn machine are required to complete the use of home theater
Function, the process is:
• direct remote control: coordinate the switch of each equipment
• popcorn opener
• drop the screen
• turn on the projector
• turn on the audio
• open DVD, select DVD
• get popcorn
• dim the lights
• play
• after viewing, turn off various equipment
tradition
Analysis of traditional ways to solve Cinema Management Problems
- In the main method of ClientTest, creating the objects of each subsystem and directly calling the relevant methods of the subsystem (object) will cause confusion in the calling process and no clear process
- It is not conducive to maintaining the operation of the subsystem in ClientTest
- Solution: define a high-level interface to provide a consistent interface for a group of interfaces in the subsystem (for example, four methods ready, play, pause and end are provided in the high-level interface to access a group of interfaces in the subsystem
- In other words, a consistent interface (interface class) is defined to shield the details of the internal subsystem, so that the caller only needs to call the interface without caring about the internal details of the subsystem
improvement
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //It's troublesome to call directly here HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(); homeTheaterFacade.ready(); homeTheaterFacade.play(); homeTheaterFacade.end(); } }
public class DVDPlayer { //Use singleton mode and hungry Han style private static DVDPlayer instance = new DVDPlayer(); public static DVDPlayer getInstanc() { return instance; } public void on() { System.out.println(" dvd on "); } public void off() { System.out.println(" dvd off "); } public void play() { System.out.println(" dvd is playing "); } //.... public void pause() { System.out.println(" dvd pause .."); } }
public class HomeTheaterFacade { //Define each subsystem object private TheaterLight theaterLight; private Popcorn popcorn; private Stereo stereo; private Projector projector; private Screen screen; private DVDPlayer dVDPlayer; //constructor public HomeTheaterFacade() { super(); this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance(); this.projector = Projector.getInstance(); this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getInstanc(); } //The operation is divided into 4 steps public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim(); } public void play() { dVDPlayer.play(); } public void pause() { dVDPlayer.pause(); } public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } }
public class Popcorn { private static Popcorn instance = new Popcorn(); public static Popcorn getInstance() { return instance; } public void on() { System.out.println(" popcorn on "); } public void off() { System.out.println(" popcorn ff "); } public void pop() { System.out.println(" popcorn is poping "); } }
public class Projector { private static Projector instance = new Projector(); public static Projector getInstance() { return instance; } public void on() { System.out.println(" Projector on "); } public void off() { System.out.println(" Projector ff "); } public void focus() { System.out.println(" Projector is Projector "); } //... }
public class Screen { private static Screen instance = new Screen(); public static Screen getInstance() { return instance; } public void up() { System.out.println(" Screen up "); } public void down() { System.out.println(" Screen down "); } }
public class Stereo { private static Stereo instance = new Stereo(); public static Stereo getInstance() { return instance; } public void on() { System.out.println(" Stereo on "); } public void off() { System.out.println(" Screen off "); } public void up() { System.out.println(" Screen up.. "); } //... }
public class TheaterLight { private static TheaterLight instance = new TheaterLight(); public static TheaterLight getInstance() { return instance; } public void on() { System.out.println(" TheaterLight on "); } public void off() { System.out.println(" TheaterLight off "); } public void dim() { System.out.println(" TheaterLight dim.. "); } public void bright() { System.out.println(" TheaterLight bright.. "); } }
Source code analysis of appearance pattern application in MyBatis framework
- Use the Configuration in MyBatis to create MetaObject objects and use the appearance mode
- Code analysis + Debug source code + schematic diagram
pattern analysis
advantage:
- The appearance mode shields the subsystem components from the client, reduces the number of objects to be processed by the client, and makes the subsystem easier to use.
- The appearance mode realizes the loose coupling relationship between the subsystem and the client, so that the change of the subsystem will not affect the client calling it, and only the appearance class needs to be adjusted.
- The modification of one subsystem has no impact on other subsystems, and the internal changes of the subsystem will not affect the appearance objects.
Disadvantages:
- Skin mode does not well restrict clients from directly using subsystem classes
- If the design is improper, adding a new subsystem may need to modify the source code of the appearance class, which violates the opening and closing principle
Precautions and details of appearance mode
- The appearance mode shields the details of the subsystem, so the appearance mode reduces the complexity of the client's use of the subsystem
- The coupling relationship between the client and the subsystem in the appearance mode makes the modules inside the subsystem easier to maintain and expand
- The rational use of appearance patterns can help us better divide the access levels
- When the system needs hierarchical design, Facade mode can be considered
- When maintaining a large legacy system, the system may have become very difficult to maintain and expand. At this time, we can consider developing a Facade class for the new system to provide a clear and simple interface of the legacy system, so that the new system can interact with the Facade class and improve reusability
- You can't use too many or unreasonable appearance patterns. It's better to use appearance patterns or call modules directly. The purpose is to make the system hierarchical and conducive to maintenance.
Mode applicable environment
Appearance mode can be used when:
- Appearance patterns can be used when you want to provide a simple access to a series of complex subsystems.
- There are great dependencies between client programs and multiple subsystems. The appearance class can decouple the subsystem from the client, so as to improve the independence and portability of the subsystem.
- In the hierarchical structure, the appearance pattern can be used to define the entrance of each layer in the system. There is no direct connection between layers, but the connection is established through appearance classes to reduce the coupling between layers.
6. Yuan sharing mode
Sharing technology is used to effectively support the reuse of a large number of fine-grained objects. The system only uses a small number of objects, and these objects are very similar, the state change is very small, and the multiple reuse of objects can be realized.
It is often used in the bottom development of the system to solve the performance problems of the system. Like the database connection pool, there are all created connection objects. Among these connection objects, we can use them directly to avoid re creation. If we don't need them, we can create one
Meta sharing mode can solve the problem of memory waste of repeated objects
The classic application scenario of meta sharing mode is pool technology. String constant pool, database connection pool and buffer pool are all applications of meta sharing mode. Meta sharing mode is an important implementation of pool technology
The meta pattern is generally used in combination with the factory pattern, and a meta factory class is included in its structure diagram
Model motivation
Realize the reuse of the same or similar objects. In the shared element mode, the place where these shared instance objects are stored is called the shared element pool.
Pattern structure
Usage examples of sharing mode
demand
For A small outsourcing project, A product display website is built for customer A. the friend of customer A feels that the effect is good and hopes to build such A product display website, but the requirements are somewhat different:
- Some customers require it to be released in the form of news
- Some clients asked to publish in the form of blog
- Some customers want to release in the form of WeChat official account.
tradition
Traditional solution website presentation project - problem analysis
- The required website structure similarity is very high, and they are not high traffic websites. If they are divided into multiple virtual spaces, it is equivalent to a lot of instance objects of the same website, resulting in a waste of server resources
- Solution: integrate into a website and share its relevant codes and data. Server resources such as hard disk, memory, CPU and database space can be shared to reduce server resources
- For the code, because it is an instance, it is easier to maintain and expand
- The above solution can be solved by using the shared element mode
improvement
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub // Create a factory class WebSiteFactory factory = new WebSiteFactory(); // The customer wants a website published in the form of news WebSite webSite1 = factory.getWebSiteCategory("Journalism"); webSite1.use(new User("tom")); // The customer wants a website published in the form of blog WebSite webSite2 = factory.getWebSiteCategory("Blog"); webSite2.use(new User("jack")); // The customer wants a website published in the form of blog WebSite webSite3 = factory.getWebSiteCategory("Blog"); webSite3.use(new User("smith")); // The customer wants a website published in the form of blog WebSite webSite4 = factory.getWebSiteCategory("Blog"); webSite4.use(new User("king")); System.out.println("Classification of websites=" + factory.getWebSiteCount()); } }
//Specific website public class ConcreteWebSite extends WebSite { //Shared part, internal state private String type = ""; //Form (type) of website publishing //constructor public ConcreteWebSite(String type) { this.type = type; } @Override public void use(User user) { // TODO Auto-generated method stub System.out.println("The publishing form of the website is:" + type + " In use .. User is" + user.getName()); } }
public class User { private String name; public User(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public abstract class WebSite { public abstract void use(User user);//Abstract method }
// The website factory class returns a website as needed public class WebSiteFactory { //Collection, which acts as a pool private HashMap<String, ConcreteWebSite> pool = new HashMap<>(); //Return a website according to the type of website. If not, create a website, put it into the pool, and return public WebSite getWebSiteCategory(String type) { if(!pool.containsKey(type)) { //Create a website and put it into the pool pool.put(type, new ConcreteWebSite(type)); } return (WebSite)pool.get(type); } //Gets the total number of site categories (how many site types are there in the pool) public int getWebSiteCount() { return pool.size(); } }
Application source code analysis of shared meta mode in JDK interger
- Meta mode in Integer
- Code analysis + Debug source code + description
pattern analysis
advantage:
- It can greatly reduce the number of objects in memory, so that only one copy of the same or similar objects can be saved in memory, which can save system resources and improve system performance.
- The external state of the sharing mode is relatively independent and will not affect its internal state, so that the sharing objects can be shared in different environments.
Disadvantages:
- The meta model makes the system complex, which needs to separate the internal state from the external state, which makes the logic of the program complex.
- In order for objects to be shared, the meta sharing mode needs to externalize part of the state of the meta sharing object, and reading the external state will make the running time longer.
Internal status and external status
- The shared meta pattern puts forward two requirements: fine granularity and shared objects. This involves internal state and external state, that is, the object information is divided into two parts: internal state and external state
- Internal state refers to the information shared by the object, which is stored in the shared meta object and will not change with the change of environment
- External state refers to a mark on which an object can depend. It is a state that changes with the environment and cannot be shared.
- For example: theoretically, there are 361 spaces in go that can hold pieces, and there may be two or three hundred pieces in each game. Because of the limited memory space, it is difficult for one server to support more players to play go games. If you use the sharing mode to process Pieces, the number of pieces can be reduced to only two instances, This solves the problem of object overhead
The design of the shared meta class is one of the key points of the shared meta model. In the shared meta class, the internal state and the external state should be handled separately. Usually, the internal state is taken as the member variable of the shared meta class, and the external state is added to the shared meta class by injection.
Notes and details of sharing mode
- In the sharing meta mode, sharing means sharing, and meta means objects
- When there are a large number of objects in the system, which consume a lot of memory, and most of the object states can be externalized, we can consider using the meta mode
- Judge with the unique identification code. If there is in memory, return the object identified by the unique identification code and store it with HashMap/HashTable
- Meta sharing mode greatly reduces the creation of objects, reduces the occupation of program memory and improves efficiency
- The meta model improves the complexity of the system. It is necessary to separate the internal state from the external state. The external state has solidification characteristics and should not change with the change of the internal state. This is what we need to pay attention to when using the sharing mode
- When using the shared element mode, pay attention to the division of internal state and external state, and a factory class is required to control it.
- The classic application scenarios of meta sharing mode are those that require buffer pools, such as String constant pool and database connection pool
Mode applicable environment
- A system has a large number of the same or similar objects, resulting in a large amount of memory consumption.
- Most states of an object can be externalized, and these external states can be passed into the object.
- When using the sharing mode, you need to maintain a sharing pool for storing sharing objects, which requires certain system resources. Therefore, it is worth using the sharing mode when you need to reuse the sharing objects for many times.
7. Agency mode
Provide a proxy to an object, and the proxy object controls the reference to the original object.
A new proxy object is introduced into the proxy mode. The proxy object acts as an intermediary between the client object and the target object. It removes the content and services that customers can't see or adds additional new services that customers need.
Provide an avatar for an object to control access to that object. That is to access the target object through the proxy object. The advantage of this is that additional function operations can be enhanced on the basis of the implementation of the target object, that is, the function of the target object can be extended.
The agent mode has different forms, mainly including three kinds of static agent, dynamic agent (JDK agent and interface agent)
Management) and Cglib agent (it can dynamically create objects in memory without implementing interfaces. It belongs to the category of dynamic agent).
- Basic introduction of static code pattern
When using a static proxy, you need to define an interface or parent class. The proxy object (i.e. the target object) implements the same interface or inherits the same parent class with the proxy object
Application examples
specific requirement
- Define an interface: ITeacherDao
- The target object TeacherDAO implements the interface ITeacherDAO
- Using the static proxy method, you need to implement ITeacherDAO in the proxy object TeacherDAOProxy
- When called, the target object is called by calling the method of the proxy object
- Special reminder: the proxy object and the target object should implement the same interface, and then call the method of the target object by calling the same method
- Basic introduction of dynamic agent mode
- The proxy object does not need to implement the interface, but the target object must implement the interface, otherwise the dynamic proxy cannot be used
- The generation of proxy object is to dynamically build proxy object in memory by using JDK API
- Dynamic agent is also called JDK agent and interface agent
API for generating proxy objects in JDK
- Package of proxy class: java.lang.reflect.Proxy
- The JDK implementation agent only needs to use the newProxyInstance method, but this method needs to receive three parameters. The complete writing method is:
static Object newProxyInstance(ClassLoader loader, Class<?>[]
interfaces,InvocationHandler h )
Basic introduction of Cglib agent mode
- Both static proxy and JDK proxy modes require the target object to implement an interface, but sometimes the target object is only a separate object and does not implement any interface. At this time, the target object subclass can be used to implement the proxy - this is Cglib proxy
- Cglib agent is also called subclass agent. It constructs a subclass object in memory to expand the function of the target object. Some books also attribute cglib agent to dynamic agent.
- Cglib is a powerful high-performance code generation package. It can extend java classes and implement java interfaces at runtime. It is widely used by many AOP frameworks, such as Spring AOP, to implement method interception
- How to select proxy mode in AOP programming:
- The target object needs to implement the interface and use JDK proxy
- The target object does not need to implement the interface and uses Cglib proxy
- The bottom layer of Cglib package is to convert bytecode and generate new classes by using bytecode processing framework ASM
Implementation steps of Cglib proxy mode
- You need to import the jar file of cglib
- Dynamically build subclasses in memory. Note that the class of the agent cannot be final, otherwise an error will be reported
java.lang.IllegalArgumentException: - If the method of the target object is final/static, it will not be intercepted, that is, additional business methods of the target object will not be executed
Model motivation
For some reasons, the client does not want or cannot access an object directly. In this case, it can achieve indirect access through a third party called "proxy"
Pattern structure
Subject: abstract topic role, which declares the common interface between real topic and proxy topic. In this way, proxy topic can be used wherever real topic is used. The client usually needs to program for abstract topic role.
RealSubject: real subject role, which defines the real object represented by the proxy role and implements real business operations in the real subject role. The client can indirectly call the operations defined in the real subject role through the proxy subject role.
Proxy: proxy topic role, which contains references to real topics, so that real topic objects can be operated at any time; Provide the same interface as the real topic role in the proxy topic role, so that the real topic can be replaced at any time; The agent topic role can also control the use of real topics, be responsible for creating and deleting real topic objects when necessary, and restrict the use of real topic objects. Generally, in the proxy topic role, the client needs to perform other operations before or after calling the referenced real topic operation, rather than simply calling the operation in the real topic object.
Examples of static agents
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //Create target object (proxied object) TeacherDao teacherDao = new TeacherDao(); //Create a proxy object and pass the proxy object to the proxy object at the same time TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao); //Through the proxy object, call the method to the proxy object //That is, the method of the proxy object is executed, and the proxy object calls the method of the target object teacherDaoProxy.teach(); } }
public class TeacherDao implements ITeacherDao { @Override public void teach() { // TODO Auto-generated method stub System.out.println(" The teacher is teaching....."); } }
//Interface public interface ITeacherDao { void teach(); // Teaching methods }
//Proxy object, static proxy public class TeacherDaoProxy implements ITeacherDao{ private ITeacherDao target; // Target object, aggregated through interface //constructor public TeacherDaoProxy(ITeacherDao target) { this.target = target; } @Override public void teach() { // TODO Auto-generated method stub System.out.println("Start agent to complete some operations..... ");//method target.teach(); System.out.println("Submit.....");//method } }
Advantages and disadvantages of static agent
- Advantages: the target function can be extended through the proxy object without modifying the function of the target object
- Disadvantages: because the proxy object needs to implement the same interface as the target object, there will be many proxy classes
- Once a method is added to the interface, both the target object and the proxy object must be maintained
Use examples of dynamic agents
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //Create target object ITeacherDao target = new TeacherDao(); //Create a proxy object for the target object, which can be converted to ITeacherDao ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); // proxyInstance=class com.sun.proxy.$Proxy0 dynamically generates proxy objects in memory System.out.println("proxyInstance=" + proxyInstance.getClass()); //The method of the target object is called through the proxy object //proxyInstance.teach(); proxyInstance.sayHello(" tom "); } }
//Interface public interface ITeacherDao { void teach(); // Training Methods void sayHello(String name); }
public class ProxyFactory { //Maintain a target Object, Object private Object target; //Constructor to initialize the target public ProxyFactory(Object target) { this.target = target; } //Generate a proxy object for the target object public Object getProxyInstance() { //explain /* * public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) //1. ClassLoader loader : Specifies the class loader used by the current target object, and the method to get the loader is fixed //2. Class<?>[] interfaces: The interface type implemented by the target object, and use the generic method to confirm the type //3. InvocationHandler h : In event processing, when executing the method of the target object, the event processor method will be triggered, and the currently executed target object method will be passed in as a parameter */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("JDK Agent start~~"); //The reflection mechanism calls the method of the target object Object returnVal = method.invoke(target, args); System.out.println("JDK Proxy submission"); return returnVal; } }); } }
public class TeacherDao implements ITeacherDao { @Override public void teach() { // TODO Auto-generated method stub System.out.println(" The teacher is teaching.... "); } @Override public void sayHello(String name) { // TODO Auto-generated method stub System.out.println("hello " + name); } }
Example of cglib proxy
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //Create target object TeacherDao target = new TeacherDao(); //Gets the proxy object and passes the target object to the proxy object TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance(); //Execute the method of the proxy object and trigger the concept method to call the target object String res = proxyInstance.teach(); System.out.println("res=" + res); } }
public class TeacherDao { public String teach() { System.out.println(" In the teacher's lecture, I am cglib Proxy, no need to implement interface "); return "hello"; } }
public class ProxyFactory implements MethodInterceptor { //Maintain a target object private Object target; //Constructor, passing in an object to be represented public ProxyFactory(Object target) { this.target = target; } //Returns a proxy object: the proxy object of the target object public Object getProxyInstance() { //1. Create a tool class Enhancer enhancer = new Enhancer(); //2. Set parent class enhancer.setSuperclass(target.getClass()); //3. Set callback function enhancer.setCallback(this); //4. Create a subclass object, that is, a proxy object return enhancer.create(); } //Overriding the intercept method will call the method of the target object @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("Cglib proxy pattern ~~ start"); Object returnVal = method.invoke(target, args); System.out.println("Cglib proxy pattern ~~ Submit"); return returnVal; } }
Variation of dynamic proxy
pattern analysis
Advantages and disadvantages of static agent
- Advantages: the target function can be extended through the proxy object without modifying the function of the target object
- Disadvantages: because the proxy object needs to implement the same interface as the target object, there will be many proxy classes
- Once a method is added to the interface, both the target object and the proxy object must be maintained