What is a design pattern
Design pattern is a general solution to the problems faced in the process of software development. It is the best practice summarized by many developers in their experiments and mistakes for a long time.
Rational use of design patterns can enhance the reliability and reusability of code, and make the code easier to be understood by others.
In general, design patterns can be divided into three categories:
- Creation patterns: provides an object creation method that "hides specific creation details".
- Structural patterns: focus on the combination of classes and objects.
- Behavioral patterns: focus on communication between objects.
Create mode
Singleton mode
brief introduction
Definition of Singleton pattern: a pattern in which a class has only one instance and the class can create the instance itself.
advantage
Public and unchanging resources have and will only have one external instance to avoid unnecessary memory occupation.
shortcoming
- The corresponding logic is written in one object, which is easy to violate the single principle.
- It cannot be extended and violates the opening and closing principle.
example
Lazy single case
The singleton is created only when the getlnstance method is called for the first time. (for example, it's double locked, but it's also a hungry man style in theory)
Multithreading needs to be synchronized every time it accesses, which will affect performance and consume more resources. This is the disadvantage of lazy singleton.
// Double locks are added to ensure thread safety, and the single thread environment can be removed public class LazySingleton { private volatile static LazySingleton instance; private LazySingleton (){} public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { if (instance == null) { instance = new LazySingleton(); } } } return instance; } }
Hungry Han style single case
A singleton instance is created when the object is initialized.
public class HungrySingleton { private static final HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
Factory method
brief introduction
As the name suggests, it is to create a factory to create new objects. There are generally four roles:
- Abstract object: defines the abstraction of an object.
- Concrete object: implement abstract object.
- Abstract factory: define the abstract of the factory and the abstract method of creating objects.
- Concrete factory: an abstract method to implement an abstract factory.
advantage
- Users only need to know the name of the specific factory to get the desired object, without knowing the specific creation process of the object.
- Flexibility is enhanced. For the creation of new type objects, you only need to write one more corresponding factory class.
- Typical decoupling framework. The high-level module only needs to know the abstract class of the product, does not need to care about other implementation classes, and meets the Demeter rule, dependency inversion principle and Richter substitution principle.
shortcoming
- For each new type of object, a specific factory needs to be added, and the number of classes will be large.
- It increases the abstraction and understanding difficulty of the system.
example
Take the milk tea shop as an example. There are pearl milk tea and fruit milk tea, so the four objects are:
- Abstract milk tea: AbstractNaicha
- Specific milk tea (pearl milk tea, fruit milk tea): ZhenzhuNaicha, ShuiguoNaicha
- Abstract naichadian factory
- Specific factory of milk tea shop (pearl milk tea shop factory, fruit milk tea shop factory): ZhenzhuNaichadianFactory, ShuiguoNaichadianFactory
// Abstract milk tea object interface AbstractNaicha{} // Bubble Tea public class ZhenzhuNaicha implements AbstractNaicha { } // Fruit milk tea public class ShuiguoNaicha implements AbstractNaicha { } // Milk tea shop Abstract Factory interface AbstractNaichadianFactory { public AbstractNaicha newNaicha(); } // Pearl milk tea shop factory to create pearl milk tea class ZhenzhuNaichadianFactory implements AbstractNaichadianFactory { public AbstractNaicha newNaicha() { return new ZhenzhuNaicha(); } } // Fruit milk tea shop factory, create fruit milk tea class ShuiguoNaichadianFactory implements AbstractNaichadianFactory { public AbstractNaicha newNaicha() { return new ShuiguoNaicha(); } } public static void main(String[] args) { // First create a pearl milk tea shop factory, and then create a pearl milk tea through the factory AbstractNaichadianFactory aF = new ZhenzhuNaichadianFactory(); AbstractNaicha zz = aF.newNaicha(); // First create a fruit milk tea shop factory, and then create a fruit milk tea through the factory AbstractNaichadianFactory bF = new ShuiguoNaichadianFactory(); AbstractNaicha sg = bF.newNaicha(); }
Abstract factory
brief introduction
Compared with the factory method pattern, there is no need for each object to have a specific factory, because an abstract factory in an abstract factory can contain multiple methods for creating objects of the same series but different types, which still have four roles:
- Abstract object: defines the abstraction of an object.
- Concrete object: implement abstract object.
- Abstract factory: define the abstract of the factory and define a variety of abstract methods to create objects of the same series but different types.
- Concrete factory: an abstract method to implement an abstract factory.
advantage
Consistent with the factory method mode.
shortcoming
Consistent with factory method mode.
example
Take the milk tea shop as an example. A milk tea shop factory can produce pearl milk tea and fruit milk tea, so the four roles are:
- Abstract milk tea: AbstractNaicha
- Specific milk tea (pearl milk tea, fruit milk tea): ZhenzhuNaicha, ShuiguoNaicha
- Abstract naichadian factory
- Specific factory of milk tea shop: NaichadianFactory
// Abstract milk tea object interface AbstractNaicha{} // Bubble Tea public class ZhenzhuNaicha implements AbstractNaicha { } // Fruit milk tea public class ShuiguoNaicha implements AbstractNaicha { } // Milk tea shop Abstract Factory interface AbstractNaichadianFactory { public AbstractNaicha newZhenzhuNaicha(); public AbstractNaicha newShuiguoNaicha(); } // Milk tea shop factory to create pearl milk tea and fruit milk tea class NaichadianFactory implements AbstractNaichadianFactory { public AbstractNaicha newZhenzhuNaicha() { return new ZhenzhuNaicha(); } public AbstractNaicha newShuiguoNaicha() { return new ShuiguoNaicha(); } } public static void main(String[] args) { // Create a milk tea shop factory AbstractNaichadianFactory aF = new ZhenzhuNaichadianFactory(); // Build pearl milk tea through the factory AbstractNaicha a = aF.newZhenzhuNaicha(); // New fruit milk tea through the factory AbstractNaicha b = aF.newShuiguoNaicha(); }
Builder pattern
brief introduction
Separating the construction of a complex object from its representation, so that the same construction process can create different representations. Such a design pattern is called builder pattern. The most important part of this model is the builder object, which is composed of four parts:
- Concrete Object: it is a concrete Object containing multiple components, and its components are created by the concrete builder.
- AbstractBuilder: it is an interface that contains abstract methods for creating various sub parts of a product, and usually includes a method getResult() that returns a complex product.
- Specific Builder: implement the builder interface and complete the specific creation method of various components of complex products.
- Director: it calls the component construction and assembly methods in the builder object to complete the creation of complex objects. The director does not involve the information of specific products.
advantage
- Good encapsulation, separation of construction and representation.
- Good expansibility, each specific builder is independent of each other, which is conducive to the decoupling of the system.
shortcoming
If the attribute of the object changes, the builder will also change, and the maintenance cost will increase.
example
Take milk tea as an example. For a cup of pure milk tea, you can choose to add pearls, coconut, pack and take away, etc. Its four components are:
- Specific milk tea: Naicha
- Abstract milk tea Builder: AbstractNaichaBuilder
- Specific milk tea Builder: NaichaBuilder
- Milk tea director: NaichaBuilderDirector
class Naicha { private boolean zhenzhu; private boolean yegua; private boolean dabao; public void setZhenzhu(boolean isAdd) { this.zhenzhu = isAdd; } public void setYegua(boolean isAdd) { this.yegua = isAdd; } public void setDabao(boolean isAdd) { this.dabao = isAdd; } } abstract class AbstractNaichaBuilder { protected Naicha naicha = new Naicha(); public abstract void buildZhenzhu(boolean isAdd); public abstract void buildYegua(boolean isAdd); public abstract void buildDabao(boolean isAdd); //Return milk tea object public Naicha build() { return naicha; } } public class NaichaBuilder extends AbstractNaichaBuilder { public void buildZhenzhu(boolean isAdd) { product.setZhenzhu(isAdd); } public void buildYegua(boolean isAdd) { product.setYegua(isAdd); } public void buildDabao(boolean isAdd) { product.setDabao(isAdd); } } class NaichaBuilderDirector { private AbstractNaichaBuilder builder; public Director(AbstractNaichaBuilder builder) { this.builder = builder; } // Whether pearl is added public AbstractNaichaBuilder zhenzhu(boolean isAdd) { builder.buildZhenzhu(isAdd); return builder; } // Coconut or not public AbstractNaichaBuilder yeguo(boolean isAdd) { builder.buildYegua(isAdd); return builder; } // Package public AbstractNaichaBuilder dabao(boolean isAdd) { builder.buildDabao(isAdd); return builder; } } public static void main(String[] args) { // New constructor AbstractNaichaBuilder builder = new NaichaBuilder(); // New Commander NaichaBuilderDirector director = new NaichaBuilderDirector(builder); // New milk tea object Naicha naicha = director .zhenzhu(true) .yeguo(true) .dabao(true) .build(); }
Prototype mode
brief introduction
Using an instance that has been created as a prototype, create a new object that is the same or similar to the prototype by copying the prototype object. Here, the prototype instance specifies the kind of object to create. There are two concepts related to this model:
- Shallow copy: it only adds a pointer to the memory address of the prototype object, only to the copied memory address. If the original address changes, the shallow copied object will change accordingly.
- Deep copy: add a pointer and apply for a new memory, make the added pointer point to the new memory, and copy all the properties of the prototype object.
java has clonable to implement shallow copy.
Behavioral model
Template method
brief introduction
Define the method skeleton in an operation, and delay some steps of the method to the subclass, so that the subclass can redefine some specific steps without changing the logical structure of the method. It is composed of three parts:
- Template method: defines the method skeleton, and the methods in the skeleton are executed in order, generally including abstract methods and concrete methods.
- Abstract method: the method of external extension, which is implemented by subclasses.
- Specific method: it is implemented by the parent class and cannot be modified by the child class.
advantage
- The parent class encapsulates the invariant part method and extends the variable part method to the child class.
- Comply with the opening and closing principle.
shortcoming
- Each implementation of extension method needs to define a subclass, and the whole class group will be large.
- If the parent class adds an abstract method, all subclasses need to add an implementation.
example
When a user goes to a milk tea shop to buy milk tea, the steps are queuing, ordering and calling. Ordering can be extended to users for customization, so the three parts are:
- Template method: buy milk tea.
- Abstract method: order.
- Specific methods: queue up and call.
public abstract class NaichaDian { // Entrance to buy milk tea public void maiNaicha() { paidui(); diancan(); jiaohao(); } // line up private void paidui() { // Realize queuing rules by milk tea shop } // Order a meal. Users decide what they want to eat public abstract void diancan(); // Call private void paidui() { // Realize the call rule by the milk tea shop } } public class User1 extends NaichaDian { @Override public void diancan(){ // I want to drink pearl milk tea } } public class User2 extends NaichaDian { @Override public void diancan(){ // I want to drink fruit milk tea } } public static void main(String[] args) { User1 u1 = new User1(); // User 1 goes to buy a cup of pearl milk tea u1.maiNaicha(); User1 u2 = new User1(); // User 2 goes to buy a cup of fruit milk tea u2.maiNaicha(); }
Intermediary model
brief introduction
Define a mediation object to encapsulate the interaction between a series of objects, so that the coupling between the original objects is loose, and the interaction between them can be changed independently. The intermediary model is also called mediation model, which is a typical application of Dimitri's law. It consists of four roles:
- Abstract mediator: it is the interface of the mediator and provides the abstract method of the interaction function between instance objects.
- Concrete Mediator role: implement the Mediator interface, define a List to manage abstract objects and coordinate the interaction between instance objects. Therefore, it depends on the abstract object class.
- Abstract object: define the interface of instance object class, save the mediator object, provide the abstract method of instance object interaction, and realize the public functions of all interacting instance object classes.
- Concrete Object class: it is the implementer of abstract Object class. When it needs to interact with other instance objects, the mediator Object is responsible for the subsequent interaction.
advantage
Reduce the coupling between objects, in line with the Demeter principle.
shortcoming
There are no obvious shortcomings.
example
The closest example is the online chat room. B oth xiaoa and Xiaob register their information in the chat room, and then send and receive messages. The messages are forwarded through the chat room, so the four roles are:
- Abstract mediator: AbstractChatMediator
- Specific intermediary * *: ChatMediator.
- Abstract object class: AbstractUser.
- Specific object classes: UserA and UserB.
public abstract class AbstractChatMediator { // Registered user public abstract void register(User user); // relay the message public abstract void relay(User user, String msg); } public class ChatMediator { private Map<Integer, User> users = new HashMap<>(); // Register users in their own user list public void register(User user) { users.put(user.id, user); } // Forward to target user public void relay(Integer id, String msg) { // By default, the user must be registered in the list users.get(id).receive(id, msg) } } public abstract class User { // Users must rely on mediation protected AbstractChatMediator mediator; public void setMediator(AbstractChatMediator mediator) { this.mediator = mediator; } // Receive message with specified id public abstract void receive(Integer id, String msg); // Send message with specified id public abstract void send(Integer id, String msg); } public class UserA extends User { public Integer id = 1; // Receive message with specified id public void receive(Integer id, String msg){ // How to deal with it is up to UserA to decide. } // Send message with specified id public void send(Integer id, String msg){ // Let intermediaries forward mediator.relay(id, msg); } } public class UserB extends User { public Integer id = 2; // Receive message with specified id public void receive(Integer id, String msg){ // How to deal with it is up to UserB to decide. } // Send message with specified id public void send(Integer id, String msg){ // Let intermediaries forward mediator.relay(id, msg); } } public static void main(String[] args) { User ua = new UserA(); User ub = new UserB(); // A sends a message to b ua.send(2, "hello"); // b sends a message to a ub.send(1, "hello"); }
Command mode
brief introduction
Encapsulating a request as an object separates the responsibility of issuing the request from the responsibility of executing the request. In this way, the two communicate through the command object, which is convenient to store, transfer, call, add and manage the command object. It consists of four roles:
- Abstract command class: it declares the interface for executing commands. It generally has the abstract method execute() for executing commands.
- Concrete Command class: it is the concrete implementation class of the abstract Command class. It has the receiver object and completes the operation to be executed by calling the receiver's function.
- Implementer / Receiver: it is the real implementer of the specific command object business by executing the relevant operations of the command function.
- Caller / requester: the sender of a request. It usually has many command objects and executes related requests by accessing the command object. It does not directly access the receiver.
advantage
It has good expansibility. It is very convenient to add or delete commands, and will not affect other classes. It conforms to the opening and closing principle.
shortcoming
A large number of specific command classes may be generated. Because each specific operation needs to design a specific command class, which will increase the complexity of the system.
example
When the milk tea shop bought something to drink, the user opened the applet, ordered a pearl milk tea and placed an order. At this time, the command of "making pearl milk tea" was passed to the milk tea shop. Later, the user felt that it was not enough to drink, ordered a fruit milk tea and placed an order. The command of "making fruit milk tea" was passed to the milk tea shop. Later, he found that eating too much would make him fat, so he cancelled the order of fruit milk tea, The order of "canceling fruit milk tea" was passed to the milk tea shop.
- Abstract command class: AbstractNaichaCommand
- Specific commands: MakeZhenzhuNaichaCommand makes pearl milk tea, makesuiguonaichacommand makes fruit milk tea, CancelShuiguoNaichaCommand cancels fruit milk tea
- Implementer / receiver: NaichadianReceiver
- Caller / requester: UserInvoker
// Milk tea shop receiver class NaichadianReceiver { // Start making milk tea public void make(String name) { } // Stop making milk tea public void stop(String name) { } } // Abstract milk tea command interface AbstractNaichaCommand { public void execute(); } // Order to make pearl milk tea class MakeZhenzhuNaichaCommand implements AbstractNaichaCommand { private NaichadianReceiver receiver; public ZhenzhuNaichaCommand(NaichadianReceiver receiver) { this.receiver = receiver; } // Give the order of making pearl milk tea to the milk tea shop @Override public void execute() { receiver.make("Pearl"); } } // Order to make fruit milk tea class MakeShuiguoNaichaCommand implements AbstractNaichaCommand { private NaichadianReceiver receiver; public ShuiguoNaichaCommand(NaichadianReceiver receiver) { this.receiver = receiver; } // Give the order of making fruit milk tea to the milk tea shop @Override public void execute() { receiver.make("Fruits"); } } // Cancel the command to make fruit milk tea class CancelShuiguoNaichaCommand implements AbstractNaichaCommand { private NaichadianReceiver receiver; public ShuiguoNaichaCommand(NaichadianReceiver receiver) { this.receiver = receiver; } // Give the order to make fruit milk tea to the milk tea shop @Override public void execute() { receiver.cancel("Fruits"); } } // User requestor class UserInvoker { // Place an order, cancel an order, comment public void action(AbstractNaichaCommand command) { command.execute(); } } public static void main(String[] args) { // First create a milk tea shop receiver NaichadianReceiver receiver = new NaichadianReceiver(); // Create another user requestor UserInvoker invoker = new Invoker(); // Single pearl milk tea invoker.action(new MakeZhenzhuNaichaCommand(receiver)); // Single fruit milk tea invoker.action(new MakeShuiguoNaichaCommand(receiver)); // Cancel fruit milk tea invoker.action(new CancelShuiguoNaichaCommand(receiver)); }
Responsibility chain model
brief introduction
In order to avoid coupling the request sender with multiple request processors, all request processors are connected into a chain by remembering the reference of the next object through the previous object; When a request occurs, it can be passed along the chain until an object processes it. It mainly includes the following roles:
- Abstract handler role: define an interface for processing requests, including abstract processing methods and a subsequent connection.
- Specific Handler role: implement the processing method of the abstract Handler to judge whether the request can be processed. If it can be processed, process it. Otherwise, transfer the request to its successor.
- Client role: create a processing chain and submit a request to the specific handler object of the chain head. It doesn't care about the processing details and the transmission process of the request.
advantage
- Reduces the coupling between objects. This mode makes an object do not need to know which object handles its request and the structure of the chain, and the sender and receiver do not need to have the clear information of each other.
- It enhances the scalability of the system. New request processing classes can be added as needed to meet the opening and closing principle.
shortcoming
- There is no guarantee that every request will be processed. Since a request has no specific receiver, it cannot be guaranteed that it will be processed. The request may not be processed until it reaches the end of the chain.
- For a long responsibility chain, the processing of requests may involve multiple processing objects, and the system performance will be affected to some extent.
- The rationality of the establishment of responsibility chain depends on the client, which increases the complexity of the client and may lead to system errors due to the wrong setting of responsibility chain, such as circular call.
example
Xiaohong's leave will be approved by managers at all levels, such as Xiaohong - Department Manager - Regional Manager - boss. The Department Manager can directly agree if it is less than three days, the regional manager can directly agree if it is less than five days, and the boss can directly agree if it is less than 30 days, but refuse if it is more than 30 days. The three roles are:
- Abstract handler: AbstractHandler
- Specific handlers: BumenJingliHandler, QuyuJingliHandler, BossHandler
- Customer class: XiaohongClient
public abstract AbstractHandler { private AbstractHandler nextHandler; public void setNextHandler(AbstractHandler handler){ this.nextHandler = handler; } public AbstractHandler getNextHandler() { return nextHandler; } // Processing leave public abstract Boolean handlerQingjia(int day); } public class BumenJingliHandler extends AbstractHandler { // No less than three days, you can agree directly, otherwise go to the next level @Override public Boolean handlerQingjia(int day) { if (day <= 3) { return true } else { return getNextHandler().handlerQingjia(day); } } } public class QuyuJingliHandler extends AbstractHandler { // No less than five days, you can agree directly, otherwise go to the next level @Override public Boolean handlerQingjia(int day) { if (day <= 5) { return true } else { return getNextHandler().handlerQingjia(day); } } } public class BossHandler extends AbstractHandler { // No less than 30 days, you can agree directly, otherwise refuse @Override public Boolean handlerQingjia(int day) { if (day <= 30) { return true } else { return false; } } } // Xiao Hong public class XiaohongClient { private AbstractHandler handle; public Xiaohong(AbstractHandler handle) { this.handle = handle; } // Leave will be given to the directly related processor first public Boolean handlerQingjia(int day) { return handle.handlerQingjia(day); } } public static void main(String[] args) { BossHandler boss = new BossHandler(); QuyuJingliHandler quyuJingli = new QuyuJingliHandler(); BumenJingliHandler bumenJingli = new BumenJingliHandler(); // Xiao Hong is directly related to the Department Manager XiaohongClient xiaohong = new XiaohongClient(bumenJingli); // The Department Manager is subordinate to the regional manager bumenJingli.setNextHandler(quyuJingli); // The subordinate of the regional manager is the boss bumenJingli.setNextHandler(boss); // Xiao Hong starts to ask for leave for 4 days. If there is no accident, the Department Manager can agree xiaohong.handlerQingjia(4); }
Strategy mode
brief introduction
This mode defines a series of algorithms and encapsulates each algorithm so that they can be replaced with each other, and the change of the algorithm will not affect the customers who use the algorithm. By encapsulating the algorithm, it separates the responsibility of using the algorithm from the implementation of the algorithm, and delegates it to different objects to manage these algorithms. The main roles of the policy model are as follows:
- Abstract strategy: defines a public interface. Different algorithms implement this interface in different ways. Environmental roles use this interface to call different algorithms, which are generally implemented by interfaces or abstract classes.
- Specific Strategy: it implements the interface defined by the abstract policy and provides specific algorithm implementation.
- Context: holds a reference to a policy class, which is finally called to the client.
advantage
The strategy mode provides perfect support for the opening and closing principle, and can flexibly add new algorithms without modifying the original code.
shortcoming
- The client must understand the differences between all policy algorithms in order to select the appropriate algorithm class in time.
- The policy mode creates many policy classes, which increases the difficulty of maintenance.
example
To travel, you can take the train, high-speed rail and plane to the tourist destination. The three roles are:
- Abstract strategy: Abstract strategy
- Specific strategies: HuocheStrategy, GaotieStrategy, feiji strategy
- Environment: Context
interface AbstractStrategy { public void execute(); } public class HuocheStrategy implements AbstractStrategy { @Override public void execute() { // Buy train tickets, prepare luggage, go to the railway station and get on the train } } public class GaotieStrategy extends AbstractStrategy { @Override public void execute() { // Buy a high-speed rail ticket, prepare your luggage, go to the high-speed rail station and get on the bus } } public class FeijiStrategy extends AbstractStrategy { @Override public void execute() { // Buy airline tickets, prepare your luggage, go to the airport station and get on the plane } } public class Context { private AbstractStrategy strategy; public Context(AbstractStrategy strategy) { this.strategy = strategy; } // Decide what travel tool strategy public void execute() { strategy.execute(); } } public static void main(String[] args) { // Let's take the high-speed railway Context context = new Context(new GaotieStrategy()); context.execute(); }
Iterator mode
brief introduction
Provides an object to sequentially access a series of data in an aggregate object without exposing the internal representation of the aggregate object. The Iterator iteration class is provided in the collection in java, which is easy to understand, so I won't explain it more.
Observer mode
brief introduction
It refers to the one to many dependency between multiple objects. When the state of an object changes, all objects that depend on it are notified and automatically updated. This mode is sometimes called publish subscribe mode and model view mode. The main roles of observer mode are as follows:
- Abstract subject: also known as abstract target class, it provides an aggregation class for saving observer objects, methods for adding and deleting observer objects, and abstract methods for notifying all observers.
- Subject: also known as the specific target class, it implements the notification method in the abstract target. When the internal state of the specific subject changes, it notifies all registered observer objects.
- Abstract Observer: it is an abstract class or interface, which contains an abstract method to update itself, which is called when receiving the change notification of a specific topic.
- Concrete Observer: implement the abstract method defined in the abstract observer to update its own state when notified of the change of the target.
advantage
It reduces the coupling relationship between the target and the observer, which is an abstract coupling relationship. Comply with the principle of dependency inversion.
shortcoming
When there are many observers, the notification of the press conference takes a lot of time, which affects the efficiency of the program.
example
Xiao Hong and Xiao Lan are concerned about the Xx official account in WeChat. When the Xx public number is updated, users will be notified of the official account of the users. The four roles are:
- Abstract topic: AbstractGongzhonghaoSubject.
- Specific subject: XxGongzhonghaoSubject.
- Abstract Observer: AbstractObserver user observer.
- Specific observers: XiaohongObserver and XiaolvObserver.
interface AbstractObserver { // Receive and process notifications of topics void receive(); } public class XiaohongObserver implements AbstractObserver { @Override public void receive() { // Xiao Hong found the official account updated, and hurried to see a wave. } } public class XiaolvObserver implements AbstractObserver { @Override public void receive() { // Xiao green found that the official account was updated, but it didn't bird it. } } interface AbstractGongzhonghaoSubject { // follow void guanzhu(AbstractObserver observer); // Cancel attention void quxiaoGuanzhu(AbstractObserver observer); // Update content void gengxin(); } public class XxGongzhonghaoSubject implements AbstractGongzhonghaoSubject { // Followers list List<AbstractObserver> observers = new ArrayList<>(); // follow @Override public void guanzhu(AbstractObserver observer) { observers.add(observer); } // Cancel attention @Override public void quxiaoGuanzhu(AbstractObserver observer) { observers.remove(observer); } // Update content and inform followers @Override public void gengxin(){ for(AbstractObserver observer: observers) { observer.receive(); } } } public static void main(String[] args) { XiaohongObserver xiaohong = new XiaohongObserver(); XiaolvObserver xiaolv = new XiaolvObserver(); AbstractGongzhonghaoSubject xxSubject = new XxGongzhonghaoSubject(); // Xiao Hong Xiao green first pays attention to a wave of Xx public number (the official account is not very good, as if official account is concerned about them, but it can be understood). xxSubject.guanzhu(xiaohong); xxSubject.guanzhu(xiaolv); // xx official account begins to update content, inform them. xxSubject.gengxin(); }
State mode
brief introduction
For stateful objects, the complex "judgment logic" is extracted into different state objects, allowing the state object to change its behavior when its internal state changes. The status mode contains the following main roles:
- Context: also known as context, it defines the interface required by the client, internally maintains a current state, and is responsible for the switching of specific state objects.
- Abstract state: defines an interface to encapsulate the behavior corresponding to a specific state in an environment object. There can be one or more behaviors.
- Concrete State: realize the behavior corresponding to the abstract State, and switch the State if necessary.
advantage
The structure is clear, and the state mode localizes the behavior related to a specific state into one state, and separates the behavior of different states to meet the "single responsibility principle".
shortcoming
- The more states, the more objects.
- The state mode does not support the opening and closing principle very well. For the state mode that can switch states, adding a new state class requires modifying the source code responsible for state transformation, otherwise it cannot switch to the new state, and modifying the behavior of a state class also requires modifying the source code of the corresponding class.
example
Asking for leave can be in three statuses: not applied, applied but not approved, and approved. Therefore, the three roles are:
- Environment class: QingjiaContext.
- Abstract state: AbstractQingjiaState
- Specific status: NotShenqingState not applied, NotShenpiState not approved, YiShenpiState approved.
public abstract class AbstractQingjiaState { private QingjiaContext context; public void setContext(QingjiaContext context) { this.context = context; } // Apply for leave public abstract bool shenqing(); // Approval application public abstract bool shenpi(); } // I haven't applied yet public class NotShenqingState extends AbstractQingjiaState { // Apply for leave and change the state of the context to the next state public bool shenqing(){ this.setContext(new NotShenpiState()); return true; } // I haven't applied yet. Of course, I haven't reached the stage of approval public bool shenpi(){ return false; } } // It hasn't been approved yet public class NotShenpiState extends AbstractQingjiaState { // Already applied, no need to apply again public bool shenqing() { return false; } // Approve the application and change the status of the context to the next status public bool shenpi(){ this.setContext(new YiShenpiState()); return true; } } // It has been approved and belongs to the final status. Therefore, false is returned for all actions, which is unsuccessful public class YiShenpiState extends AbstractQingjiaState { public bool shenqing() { return false; } public bool shenpi() { return false; } } public class QingjiaContext { private AbstractQingjiaState state; // When initializing the leave context, the leave status is still not applied. public QingjiaContext() { this.state = new NotShenqingState(); } public void setState(AbstractQingjiaState state) { this.state = state } public void getState() { return state } // Apply for leave public bool shenqing(){ return this.state.shenqing(); } // Approval application public bool shenpi(){ return this.state.tongguo(); } } public static void main(String[] args) { // Two scenarios can be set // The normal process is to apply, approve and obtain the leave status. The final status must be approved QingjiaContext c1 = new QingjiaContext(); c1.shenqing(); c1.shenpi(); c1.getState(); // The abnormal process is directly approved, and then the leave status is obtained. The final status is not applied QingjiaContext c1 = new QingjiaContext(); c1.shenpi(); c1.getState(); // In fact, each request behavior will return a Boolean value, which can be used to judge whether the current leave behavior process is effective. }
Memo mode
brief introduction
On the premise of not destroying the encapsulation, capture the internal state of an object and save the state outside the object, so that the object can be restored to the original saved state when necessary. This mode is also called snapshot mode. Mode has three roles:
- Originator: records the internal status information at the current time, provides the function of creating memos and recovering memo data, and realizes other business functions. It can access all the information in memos.
- Memo: it is responsible for storing the internal status of the initiator and providing these internal status to the initiator when necessary.
- Caretaker: manages memos and provides the function of saving and obtaining memos, but it cannot access and modify the contents of memos.
advantage
Provides a mechanism to restore state. When users need it, they can easily restore the data to a historical state.
shortcoming
Large resource consumption.
example
The Archiving Mechanism of the game is the memo mode. The game provides two functions: archiving and file reading. The archive file saves the state of the game progress, and the archive manager manages the archive file. So the three roles are:
- Initiator: GameOriginator
- Memo: CundangMemento
- Manager: CundangCaretaker
public class CundangMemento { } public class CundangCaretaker { private List<CundangMemento> mementos = new ArrayList<>(); // Get archive list public List<CundangMemento> getMementos() { return mementos; } // Save Archive public void save(CundangMemento memento) { mementos.add(memento); } // File reading public CundangMemento dudang(Integer state){ return caretaker.get(state); } // You can add, overwrite and delete files later } public class GameOriginator { private CundangCaretaker caretaker = new CundangCaretaker(); // Get archive list public List<CundangMemento> getMementos() { return caretaker.getMementos(); } // file public void save() { return new CundangMemento(); } // File reading public CundangMemento dudang(Integer state){ return caretaker.dudang(state); } } public static void main(String[] args) { // Create game (with archive manager) GameOriginator originator = new GameOriginator(); // file originator.save(); // Get archive list originator.getMementos(); // You can select the archive you want to read according to the archive list. Here, the step of obtaining the subscript of the list is omitted, and you can directly assume a 1 to read the file. originator.dudang(1); }
Interpreter mode
brief introduction
Define a language for the analysis object, define the grammatical representation of the language, and then design a parser to interpret the sentences in the language. In other words, use the way of compiling language to analyze the examples in the application. This pattern implements the interface of grammar expression processing, which interprets a specific context. Mode has the following roles:
- Abstract Expression: defines the interface of the interpreter and specifies the interpretation operation of the interpreter, mainly including the interpretation method interpret().
- Terminal Expression: it is a subclass of abstract expression, which is used to realize the operations related to terminals in grammar. Each terminal in grammar has a specific Terminal Expression corresponding to it.
- Nonterminal Expression: it is also a subclass of abstract expression, which is used to realize the operations related to nonterminal in grammar. Each rule in grammar corresponds to a Nonterminal Expression.
- Context: it usually contains the data or common functions required by each interpreter. It is generally used to transfer the data shared by all interpreters. Subsequent interpreters can obtain these values from here.
- Client (Client): the main task is to translate the sentences or expressions that need analysis into the abstract syntax tree described by the interpreter object, then invoke the interpreter's interpretation method, and of course, can indirectly access the interpreter's interpretation method through the role of the environment.
Interpreter mode is rarely used in actual software development, because it will cause problems such as efficiency, performance and maintenance, so it will not be discussed here. Interested children's shoes can consult relevant materials by themselves.
Visitor mode
brief introduction
The operations that act on each element in a data structure are separated and encapsulated into independent classes, so that they can add new operations that act on these elements without changing the data structure, and provide a variety of access methods for each element in the data structure. It separates the data operation from the data structure. It is the most complex pattern in the behavior pattern. Modes have several roles:
- Abstract visitor: define an interface to access specific elements. Each specific element class corresponds to an access operation visit(). The parameter type in the operation identifies the specific element to be accessed. Theoretically, the number of visit methods is the same as the number of elements. Therefore, the visitor mode requires the type of elements to be stable. If you often add or remove element classes, it will inevitably lead to frequent modification of AbstractVisitor interface. If this happens, it indicates that the visitor mode is not suitable for use.
- Concrete Visitor: implement each access operation declared in the abstract Visitor role and determine what the Visitor should do when accessing an element.
- Abstract element: declare an interface containing the accept operation accept(), and the accepted visitor object is used as the parameter of the accept() method.
- Concrete Element: implement the accept() operation provided by the abstract Element role, and its method body is usually visitor Visit (this). In addition, the specific elements may also contain relevant operations of their own business logic.
- Object structure: it is a container containing element roles. It provides methods for visitor objects to traverse all elements in the container. It is usually implemented by aggregate classes such as List, Set and Map.
Applicable scenario
You can usually consider using visitor mode in the following cases:
- Object structure is relatively stable, but its operation algorithm often changes.
- The objects in the object structure need to provide a variety of different and irrelevant operations, and the changes of these operations should not affect the object structure.
- The object structure contains many types of objects. You want to perform some operations on these objects that depend on their specific types.
advantage
- Good scalability. It can add new functions to the elements in the object structure without modifying the elements in the object structure.
- Good reusability. Visitors can define the general functions of the whole object structure, so as to improve the reuse degree of the system.
- Good flexibility. Visitor mode decouples the data structure from the operations acting on the structure, so that the operation set can evolve relatively freely without affecting the data structure of the system.
- Comply with the principle of single responsibility. Visitor mode encapsulates the relevant behaviors to form a visitor, so that the function of each visitor is relatively single.
shortcoming
- Destroy the package. In the visitor pattern, specific elements publish details to visitors, which destroys the encapsulation of objects.
- Violation of the dependency inversion principle. The visitor pattern relies on concrete classes instead of abstract classes.
example
There are two kinds of milk tea in the milk tea shop, pearl milk tea and fruit milk tea. Non members can buy them at the original price, while members can get a 10% discount. So these roles are:
- Abstract visitor: AbstractVisitor
- Specific visitors: non member visitors of NoHuiyuanVisitor and member visitors of HuiyuanVisitor
- Abstract element: AbstractNaichaElement
- Specific elements: ZhenzhuNaichaElement pearl milk tea, ShuiguoNaichaElement fruit milk tea
- Object structure: naichdianstructure milk tea shop
interface AbstractVisitor { // Visitors to buy pearl milk tea price double visit(ZhenzhuNaichaElement elemen); // Visitors to buy fruit milk tea prices double visit(ShuiguoNaichaElement elemen); // If there are new types of milk tea in the future, they can be added here } // Non members buy all kinds of milk tea at the original price public class NoHuiyuanVisitor implements AbstractVisitor { public double visit(ZhenzhuNaichaElement elemen){ return elemen.getPrice(); } public double visit(ShuiguoNaichaElement elemen){ return elemen.getPrice(); } } // Members buy all kinds of milk tea at a 10% discount public class NoHuiyuanVisitor implements AbstractVisitor { public double visit(ZhenzhuNaichaElement elemen){ return elemen.getPrice() * 0.9; } public double visit(ShuiguoNaichaElement elemen){ return elemen.getPrice() * 0.9; } } public abstract class AbstractNaichaElement { private double price; public AbstractNaichaElement(double price){ this.price=price; } public double getPrice() { return price; } abstract double accept(AbstractVisitor visitor); } public class ZhenzhuNaichaElement extends AbstractNaichaElement { public ZhenzhuNaichaElement(double price) { super(price); } @Override public double accept(AbstractVisitor visitor) { return visitor.visit(this); } } public class ShuiguoNaichaElement extends AbstractNaichaElement { public ShuiguoNaichaElement(double price) { super(price); } @Override public double accept(AbstractVisitor visitor) { return visitor.visit(this); } } public class NaichadianStructure { List<AbstractNaichaElement> elements = new ArrayList<>(); // Here, you can traverse the milk tea object and obtain the price of all milk tea through the passed in member type public void accept(AbstractVisitor visitor) { for(AbstractNaichaElement element: elements) { // Get different prices element.accept(visitor); } } public void add(AbstractNaichaElement element) { elements.add(element); } } public static void main(String[] args) { // New milk tea shop NaichadianStructure naichadian = new NaichadianStructure(); // New 2 kinds of milk tea naichadian.add(new ZhenzhuNaichaElement(10)); naichadian.add(new ShuiguoNaichaElement(10)); // Non members view the price of all dairy products naichadian.accept(new NoHuiyuanVisitor()); // Members view the price of all dairy products naichadian.accept(new HuiyuanVisitor()); }
Structural model
Adapter mode
brief introduction
Convert the interface of a class into another interface desired by the customer, so that those classes that cannot work together due to incompatible interfaces can work together. The adapter pattern contains the following main roles:
- Target interface: the interface expected by the current system business. It can be an abstract class or interface.
- Adapter: it is the component interface in the existing component library that is accessed and adapted.
- Adapter: it is a converter that converts the adapter interface into the target interface by inheriting or referencing the adapter object, so that customers can access the adapter in the format of the target interface.
advantage
- Reusing the existing classes, programmers do not need to modify the original code and reuse the existing adapter classes.
- Decoupling the target class from the adapter class solves the problem of inconsistent interfaces between the target class and the adapter class.
- In many business scenarios, it conforms to the opening and closing principle.
shortcoming
Increase the difficulty of code reading and reduce the readability of code. Excessive use of adapters will make the system code messy.
example
Apple data cable can charge and bind things. Now Huawei mobile phones charge, but now only apple data cable and converter. The converter supports Apple data cable to Android data cable, so plug the apple cable into the converter, and Huawei can charge through apple data cable. These roles are:
- Target interface: Huawei target
- Adapter (): PingguoAdaptee
- Adapter (): pingguo2huawei adapter apple to Huawei
interface HuaweiTarget { void chongdian(); } public class PingguoAdaptee { public void chongdian() { } // Apple data cable is not only rechargeable, but sometimes it can be used as a rope to tie things public void bangDongxi() { } } public class Pingguo2HuaweiAdapter extends PingguoAdaptee implements HuaweiTarget { @Override public void chongdian() { // The converter changes the charging function of apple to one suitable for Android } } public static void main(String[] args) { Pingguo2HuaweiAdapter adapter = new Pingguo2HuaweiAdapter(); // Charge Huawei adapter.chongdian(); // Tie things adapter.bangDongxi(); }
Bridging mode
brief introduction
Separate abstraction from implementation so that they can change independently. It is implemented by replacing inheritance relationship with composition relationship, which reduces the coupling between abstraction and implementation. The bridge mode contains the following main roles:
- AbstractObject: defines an abstract class and contains a reference to the implementation object.
- Refined abstracttobject: it is a subclass of the abstract role, which implements the business methods in the parent class and calls the business methods in the abstract role through the combination relationship.
- Abstractimplementer: defines the interface to implement the role, which can be called by the extended abstract role.
- Implementer: give the concrete implementation of the implementation role interface.
Applicable scenario
A common use scenario of bridging mode is to replace inheritance. Inheritance itself is highly intrusive (parent code invades subclasses), resulting in bloated subclasses. Therefore, combination / aggregation is preferred.
The bridge pattern is characterized by the separation of abstraction and implementation, so it is suitable for
- A class has two or more independently changing dimensions, and both dimensions need to be extended
- Scenarios where inheritance is not desired or applicable
advantage
- Separation of abstraction and implementation, strong scalability
- Comply with the opening and closing principle
shortcoming
nothing
example
Milk tea shops sell milk tea, which is divided into pearl milk tea and fruit milk tea according to type, and medium cup and large cup according to weight., As long as the category is carried in the component, you can combine the category and component to buy milk tea, so the four roles are:
- Abstraction: AbstractFenliang
- Extension abstraction: ZhongFenliang, DaFenliang
- Implementation: AbstractZhonglei
- Specific implementation: ZhenzhuZhonglei, ShuiguoZhonglei
interface AbstractZhonglei { void add(); } public class ZhenzhuZhonglei implements AbstractZhonglei { public void add(){ // Bubble Tea } } public class ShuiguoZhonglei implements AbstractZhonglei { public void add(){ // Fruit milk tea } } public abstract class AbstractFenliang { protected AbstractZhonglei zhonglei; public Abstraction(AbstractZhonglei zhonglei) { this.zhonglei = zhonglei; } abstract void add(); } public class ZhongFenliang AbstractFenliang { public Abstraction(AbstractZhonglei zhonglei) { super(zhonglei); } public void add(){ // Medium cup // +Kind zhonglei.add(); } } public class DaFenliang AbstractFenliang { public Abstraction(AbstractZhonglei zhonglei) { super(zhonglei); } public void add(){ // Big cup // +Kind zhonglei.add(); } } public static void main(String[] args) { // Large pearl milk tea new DaFenliang(new ZhenzhuZhonglei()); // Medium cup fruit milk tea new ZhongFenliang(new ShuiguoZhonglei()); }
Combination mode
brief introduction
It is also called part whole mode. It is a mode of combining objects into a tree hierarchy, which is used to represent the "whole part" relationship and enable users to have consistent access to single objects and combined objects.
In theory, it is a tree data model, which is not used much. Interested children can access the Internet by themselves.
Decoration mode
brief introduction
The pattern of dynamically adding some responsibilities (i.e. adding additional functions) to the object without changing the existing object structure. Decorator mode mainly includes the following roles:
- Abstract component: defines an abstract interface to specify an object ready to receive additional responsibilities.
- Concrete component: implement abstract components and add some responsibilities to them through decorative roles.
- Abstract decorator: inherits abstract components and contains instances of specific components. You can extend the functions of specific components through its subclasses.
- Decorator: implement the relevant methods of abstract decoration and add additional responsibilities to specific component objects.
advantage
- Different effects can be achieved by using different decorative classes and the arrangement and combination of these decorative classes
- The decorator mode fully complies with the opening and closing principle
shortcoming
nothing
example
You can ask the boss to add eggs and ham... In this case, the four roles are:
- Abstract component: AbstractShouzhuabingComponent
- Specific component: ShouzhuabingComponent
- Abstract shouzhuabingdecorator
- Specific decoration: JidanDecorator, huotudecorator
interface AbstractShouzhuabingComponent { public void add(); } class ShouzhuabingComponent implements AbstractShouzhuabingComponent { public void add() { // Add a grab cake } } class AbstractShouzhuabingDecorator implements AbstractShouzhuabingComponent { private AbstractShouzhuabingComponent component; public AbstractShouzhuabingDecorator(AbstractShouzhuabingComponent component) { this.component = component; } public void add() { component.add(); } } // egg class JidanDecorator extends AbstractShouzhuabingDecorator { public JidanDecorator(AbstractShouzhuabingComponent component) { super(component); } public void add() { super.add(); // Add an egg to the other decorations } } // Ham class HuotuiDecorator extends AbstractShouzhuabingDecorator { public HuotuiDecorator(AbstractShouzhuabingComponent component) { super(component); } public void add() { super.add(); // Add ham to other decorations } } public static void main(String[] args) { // Let's grab a cake first ShouzhuabingComponent shouzhuabing = new ShouzhuabingComponent(); // Add an egg JidanDecorator jidan = new JidanDecorator(shouzhuabing); // Add a ham HuotuiDecorator huotui = new HuotuiDecorator(jidan); }
Appearance mode
brief introduction
Also known as facade mode, it is a mode that makes these subsystems more accessible by providing a consistent interface for multiple complex subsystems. This mode has a unified interface to the outside, and the external application does not need to care about the specific details of the internal subsystem, which will greatly reduce the complexity of the application and improve the maintainability of the program. Three roles:
- Facade: provide a common interface for multiple subsystems.
- Sub System: realize some functions of the system, and customers can access it through appearance roles.
- Client: access the functions of each subsystem through a appearance role.
advantage
- It reduces the coupling between the subsystem and the client, so that the change of the subsystem will not affect the client class calling it.
shortcoming
- Adding a new subsystem may require modifying the appearance class or the source code of the client, which violates the "opening and closing principle".
example
When I was a child in primary school, I had no money. I directly asked my mother for 50 cents to buy spicy strips, so my mother opened the cabinet, opened the money can, took out 50 cents and gave it to me. For me, I only need to know that I asked my mother for money, and I don't need to know how my mother changed money. So the three roles are:
- Appearance: MotherFacade
- Subsystems: Dakaiguizi, DakaiCunqianguang, Nachu5
- Client: MeClient
public class MotherFacade { private Dakaiguizi dakaiguizi = new Dakaiguizi(); private DakaiCunqianguang dakaiCunqianguang = new DakaiCunqianguang(); private Nachu5 nachu5 = new Nachu5(); // pay public void geiqian() { // Open the cabinet, open the piggy bank and take out 50 cents dakaiguizi.do(); dakaiCunqianguang.do(); nachu5.do(); } } public class Dakaiguizi { public void do(){ // Open the cabinet } } public class DakaiCunqianguang { public void do(){ // Open the piggy bank } } public class Nachu5 { public void do(){ // Take out fifty cents } } public class MeClient { // Ask your mother for money public void yaoqian(MotherFacade mother){ mother.geiqian(); } } public static void main(String[] args) { MotherFacade mother = new MotherFacade(); MeClient me = new MeClient(); me.yaoqian(mother); }
Sharing element mode
brief introduction
Sharing technology is used to effectively support the reuse of a large number of fine-grained objects. The main roles of Xiangyuan mode are as follows.
- Abstract flyweight: it is the base class of all concrete shareware classes and the public interface to be implemented by the concrete shareware specification.
- Flyweight: implement the interface specified in the abstract role of flyweight.
- FlyweightFactory: responsible for creating and managing the role of FlyweightFactory. When a customer object requests a meta object, the meta factory checks whether there are qualified meta objects in the system, and if so, provides them to the customer; If it does not exist, create a new meta object.
The extensible ones are the internal state and external state, that is, the element sharing part and the non element sharing part. java's thread pool uses the shared element mode. The non shared element part is the configuration of the thread pool, and the shared element part is the reused thread pool.
advantage
Only one copy of the same object is saved, which reduces the number of objects in the system, thus reducing the pressure on memory caused by fine-grained objects in the system.
shortcoming
Reading the status takes more time.
example
Mom cooks in the kitchen, and then you can fry eggs and cabbage with the same domestic iron pot. Of course, mom also has an imported iron pot. So the three roles are:
- Abstract Yuanxiang role: AbstractZuocaiFlyweight Yuanxiang role in cooking
- Specific enjoy yuan: TieguoFlyweight iron pot enjoy yuan
- Xiangyuan factory: TieguoFlyweightFactory
// Because it may be extended to internal and external states, it is an abstract class as the parent class public abstract class AbstractZuocaiFlyweight { abstract void do(); } public class TieguoFlyweight extends AbstractZuocaiFlyweight { private String name; public TieguoFlyweight(String name) { this.name = name; } @Override public void do(){ // The iron pot began to stir fry } } public class TieguoFlyweightFactory { Map<String, TieguoFlyweight> tieguos = new HashMap<>(); public TieguoFlyweight getTieguoFlyweight(String name) { if tieguos.containsKey(name) { return tieguos.get(name); } else { return tieguos.put(name, new TieguoFlyweight()) } } } public static void main(String[] args) { TieguoFlyweightFactory factory = new TieguoFlyweightFactory(); // Mother wants to take out the domestic iron pot to fry eggs and cabbage factory.getTieguoFlyweight("Domestic iron pot"); // I thought it tasted bad and took out an imported iron pot to fry carrots factory.getTieguoFlyweight("Imported iron pot"); }
proxy pattern
brief introduction
For some reason, an object needs to be provided with a proxy to control access to the object. The main roles of agent mode are as follows:
- Abstract subject: business methods implemented by declaring real topics and proxy objects through interfaces or abstract classes.
- Real Subject: it realizes the specific business in the abstract Subject. It is the real object represented by the proxy object and the object to be referenced finally.
- Proxy: it provides the same interface as the real topic. It contains references to the real topic. It can access, control or expand the functions of the real topic.
advantage
- The proxy object can extend the function of the target object
- Proxy mode can separate the client from the target object, reduce the coupling of the system to a certain extent, and increase the scalability of the program
example
Xiaoming wants to go to France to buy French perfume, but he doesn't want to buy it himself. So he buys a French perfume and buys perfume from France. So the three roles are:
- Abstract topic: AbstractFaguoSubject
- Real subject: FaguoSubject
- Proxy: DaigouProxy
interface AbstractFaguoSubject { void buy(String name); } public class FaguoSubject implements AbstractXiangshuiSubject { public void buy(String name){ // What do you buy in France } } public class DaigouProxy { private AbstractFaguoSubject faguo; public DaigouProxy(AbstractFaguoSubject faguo) { this.faguo = faguo; } public void buy(String name) { this.faguo.buy(name); } } public static void main(String[] args){ // Create a French AbstractFaguoSubject faguo = new FaguoSubject(); // Create a purchasing agent DaigouProxy daigou = new DaigouProxy(faguo); daigou.buy("French perfume"); // You can buy anything you want to buy in France in the future. }