Design Pattern ---- behavioral patterns

Source: Internet
Author: User

Behavioral patterns (behavior pattern) involves the assignment of tasks between objects and the algorithm for completing these tasks. It not only describes the object pattern or class design pattern, it also describes the interaction mode between objects or classes. Using this design mode can free programmers from complicated control flows and focus only on interaction between objects.

Behavioral patterns can be divided into two types of design patterns: Behavioral class patterns (behavior class mode) and behavioral object patterns (behavior object mode ).

Behavioral class patterns assigns tasks among classes by means of class inheritance. Such modes include template method pattern (template method pattern) and interpreter pattern (Interpreter pattern ).

Behavioral object patterns allocates tasks between objects through combinations of objects instead of class inheritance. It describes a group of peer objects that complete a task through collaboration, this task cannot be completed by any of the objects. Since objects need to be coordinated, an object may need to understand other objects in the same group. An important problem with this design pattern is how to make the peer objects understand each other. These design patterns mainly include Mediator Pattern, chain of responsibility pattern, Observer pattern, and strategy pattern ), command pattern, state pattern, visitor pattern, iterator pattern, and memento pattern ).

Chain of responsibility pattern:

Chain of responsibility pattern (responsible chain mode) introduces multiple objects to process requests. Any object may process one request, but the request does not know which object to process, in this way, the request sender and receiver are decoupled. Chain of responsibility pattern connects these receiving objects into a chain and transmits requests along this chain to know that an object is processing it.

There is usually a base class handler, which has a virtual method handle and a succesor. Handle is implemented by default to pass requests to succesor for processing. It can also be implemented by its subclass. After each subclass receives the request, it first determines whether it can process the request. If it can, it directly processes the request. Otherwise, it calls the handle method of the base class handler and passes the request to succesor for processing.

The general structure of this design pattern is as follows:



Command pattern:

Command pattern encapsulates a request as an object. Different objects send different requests to different request recipients to get different responses.

The graphic interface software we use usually has a button. When we click a button, the software usually responds. This process can be seen as: We click the button, the button senses (and may send a request to the recipient), and then responds. To implement this function, you can simply write the code in the response process to the button. Each time the button is pressed, the self-contained Response Program of the button will be called to respond. However, this process has an obvious drawback: Each button must implement its response function, and if the functions of the two buttons are similar or even identical, it is also necessary to implement the Response Program for the two buttons separately, seriously affecting the reusability of the code, and the implementation and maintenance are extremely complicated. In addition, the request sender and receiver are bundled, and the coupling degree is very high.

Later, the callback function (callback function) emerged, which is a process-oriented mechanism that effectively solves the code reuse problem and decouples the request sender and receiver. However, using Command pattern to implement this function is an object-oriented mechanism. Compared with callback function, command can be regarded as a whole object. The object not only contains the response method, it also includes some attributes (such as status). With these attributes, the command can not only couple the request sending and receiving response processes like the callback function, it can also record the records of sent and response commands, or even cancel the operation. Because command regards commands as a whole, you can also combine these command objects into a complex object to implement complex operations.

In command pattern, there is a command base class that contains an abstract interface execute. On the basis of this base class, you can derive many concrete sub-classes concretecomand, each subclass can implement the execute method again, and each subclass can also define its own receiver (the receiver of the command), and can also define State attributes as needed. Each time you define a button, attach a specific concretecommand instance to this button and click the button to call the execute method in the concretecommand instance to send the request.

The general structure of Command pattern is as follows:



Interpreter pattern:

Interpreter pattern (Interpreter mode) is mainly used for similar parsing syntax.

Given a syntax and the statements in the syntax, calculate the specific values of the statements in the syntax. There are multiple ways to solve this problem, such as converting grammar directly into a state machine, and the efficiency is higher than interpreter pattern. Moreover, in interpreter pattern, each syntax rule must at least implement a corresponding class. Once the grammar rules are complex, using interpreter pattern will lead to many classes that are uncontrollable. However, the implementation of interpreter pattern is relatively simple.

Interpreter pattern consists of context and expression. Expression indicates an expression. It can also be understood as a statement following a certain article. Context refers to the current context, for example, the specific value of a variable. The value can be used to calculate the value of the entire expression or sentence.

In expression, there is first an expression base class named abstractexpression, which contains a common abstract interface interpret (context) ---- parses the value of the expression based on the current context. The terminalexpression (terminalexpression, which can be used as the end of a sentence) and nonterminalexpression (non-terminalexpression, which cannot be used as the end of a sentence) are derived ). Nonterminalexpression can also be used to derive an exclusive expression subclass. In this way, a syntax tree is formed. The syntax tree can not only parse the values of specific expressions, but also add specific operations as needed.

The general structure of interpreter pattern is as follows:



Iterator pattern:

The iterator pattern (iterator mode) provides an access method for the data set object. In this way, some interfaces or attribute values of the data set object are not exposed.

In Java, list can be regarded as an iterator, which is a base class (an interface in Java ), this class provides abstract interfaces for accessing specific data set objects, such as get and Add, it has two common sub-classes (or two common classes implement the list Interface), arraylist and sorted list. These two sub-classes respectively store data set objects of different structures, one is an array and the other is a linked list. However, they can rewrite the abstract interfaces provided by the list class to implement the functions required by their specific classes. Then, when accessing data set objects of different structures, you can access it through the same form of iterator interface.

Iterator pattern consists of aggregate and iterator. The first part of aggregate is a data collection class, which provides some universal operation interfaces for data sets. Based on aggregate, various specific sub-classes can be derived to represent different data set structures, the specific subclass can implement the abstract interface provided by aggregate again. The second part is the iterator used to access aggregate. It provides some common data access interfaces, such as first () and next, similarly, iterator can derive various specific iterator subclasses, and each iterator subclass can re-implement the abstract methods provided by iterator as needed.

The general structure of iterator pattern is as follows:



Mediator Pattern:

Mediator Pattern defines an object, which encapsulates the interaction methods of a series of objects, thus reducing the coupling between interaction objects.

Object-Oriented Programming usually encourages a large task to be divided into many small tasks, each of which completes their respective functions through interaction with other small tasks. This method has three main disadvantages: 1. This produces connections (or references) between many small task objects. In the worst case, even two small task objects have a pair of connections (including references of their respective objects). If the number of small tasks is N, the number of connections is n (n-1) in the worst case ). 2. When any small task object changes, the behavior of other small task objects may be affected. In this case, the changed small object needs to notify all small objects that reference it, in addition, each small task object needs to interact with other small task objects, which makes the design of the small task class very complicated. 3. If this method is used, the interaction between small objects is re-implemented for each large task. Specifically, when a small object changes, the objects affected in different large tasks will also be different, so you need to redesign the small task object. If we use the subclass-derived method to implement it, we may need to derive n subclasses at the worst. This fully demonstrates the high coupling between objects in this method.

Mediator Pattern contains an important object called mediator (adapter ). After dividing a large task into multiple small task objects, these small task objects only need to interact with the mediator, which reduces the number of connections between objects, A maximum of N connections are required. When a small task object changes, you only need to send the change information to the Mediator object. this object will naturally adjust other small task objects through the changed information. The interaction method is very simple. In addition, if you want to complete another major task, you only need to re-design a mediator object, instead of re-designing n sub-classes of small tasks.

Mediator Pattern mainly concentrates some distributed work on a mediator object. Its structure is as follows:


Colleague is the small task object class mentioned above. The interaction method in Mediator Pattern is roughly as follows:



Memento pattern:

Memento pattern can be used to extract and save the internal state of the data object, so that the data object can be restored to a previous state as needed, it does not need to break the object encapsulation.

An undo (UNDO) operation is often used when you use a text or graphic editor. To complete this operation, you must first know what the Undo content will be, in this case, you need to record and save the status before undo. However, normally, an object encapsulates its own attributes so that external access to these attributes is inconvenient or even inaccessible. Exposing these attributes will damage the encapsulation of the object. This is a trade-off. One is to maintain encapsulation without exposing attributes, so that the state of the data object cannot be recorded; the other is to expose attributes to record the state of the data object, however, encapsulation is exposed. Memento pattern can solve this problem perfectly.

Memento, as its name implies, indicates a memorandum. When the data object (which can be viewed as originator) changes and is required, you can create a memento to save the current object status, during the continuous change of Data Objects, memento is constantly created to save the state of data objects at key time points. Therefore, memento can also be viewed as a snapshot of the data object ). When the data object needs to be restored to a previous state, call the corresponding memento to view the State attributes in it to restore to the previous state. You can save memento into a certain structure as needed. You may wish to unify this structure into a caretaker, which can be simply designed as a queue. Currently, it only shows how to use memento pattern to restore Data Objects. However, the encapsulation of Data Object Attributes is not discussed. In many object-oriented languages, you can set access permissions. In C ++, you can set the attributes of the Data memento object to protected and set originator to a friend class, then, when the memento object is restored, originator can access the attributes of the memento object, while other places such as caretaker can only save or pass the memento object, but cannot access the attributes of the memento object, in this way, the encapsulation of Data Objects is maintained.

The general structure of memento pattern is as follows:



Observer pattern:

The Observer Pattern Defines a one-to-many dependency. When the State of an object is changed, all objects dependent on it are automatically notified and changed accordingly.

In object-oriented systems, we usually encourage a system to be divided into many small parts, which facilitates code reuse and inter-module independence. However, after being divided into many small modules, data consistency problems may occur between these small modules. A module changes the data, and other objects may reference the data. If other objects do not update the data in real time, the values of the same data may be inconsistent. To solve this problem, each small module can be connected to other small modules that may be related (reference the same data). When a small module changes one data, this will notify other small modules that reference this data to update the data. Although this can solve the problem temporarily, there are several disadvantages: 1. many connections are required for the entire system. Assume that N is the number of modules. In extreme cases, N * (n-1) connections are required, you need to check which modules reference the same data and establish a connection with them. This makes the entire system very complex, difficult to maintain, and the efficiency of the entire system is not high. 2. each module needs to store other modules related to itself. You need to know which modules you are related to and implement interaction with other modules on your own, this not only requires a large amount of memory, the code implementation of the module is complicated, and the code reuse rate is not high. 3. The coupling between modules is extremely high, so that all modules of the entire system are combined, and the independence is very poor. Observer pattern can solve this problem well: regard all modules as observer (observer), and create a new module as the data storage pool (subject ), all the observers only need to establish a connection with the subject, and all the observers can only read data from the subject. Each time an observer changes data, the subject notifies other objects. Other Objects change the corresponding data values as needed. In this way, the number of connections of the entire system becomes small, easy to maintain, and the interaction between modules is very simple and convenient (only need to interact with an object (subject object ), the independence between modules is also very high. The use of Observer Pattern not only solves this problem, but also perfectly overcomes the shortcomings of the above methods.

Specifically, for example, a statistical table can be displayed in many ways, such as a sector statistical chart, a column Statistical Chart, and a statistical table. The display mode can be viewed as different display modules of a system. These display modules use the data of the same statistical table. If you use a simple method, you need to establish a connection between different display modules. When a display module changes data, you need to notify other display modules, however, we have already discussed the disadvantages of this method. If you use observer pattern to solve this problem, you only need to create a subject class to save the statistical table data. Other display modules retrieve data from the subject class. When a display module changes the data, you only need to notify subject and subject to notify other display modules.

In observer pattern, there is a subject class that can be viewed as a data interaction hub. Generally, three main abstract interfaces are provided: attach (add observer), detach (delete observer ), notify (notifies observer to update data ). On the basis of the subject class, you can derive a specific subclass concretesubject as needed. each subclass has its own data status and implements data status operations. In addition, the observer class is also included. This class usually provides an abstract interface for update. Similarly, the observer class can derive other concrete sub-classes concreteobserver. concreteobserver usually has its own data status and implements update again. When a data change occurs, the subject notifies each observer through notify y to call their respective update to update the data status.

The structure of the observer pattern is as follows:



State pattern:

State pattern encapsulates the internal state of an object into a State object. When the State object of this object changes, the running Modes of some of its methods will also change.

Sometimes, an object needs to determine its operation behavior and state transfer based on its current attribute status. For example, if the status of an object is a, it should perform the optiona operation and switch the status to B. If the status is B, it should perform the optionb operation, and switch the status to. The entire process involves two steps: 1. perform appropriate operations based on the status; 2. Determine the transfer status. There are two methods to achieve this function: 1. determine the specific status through the conditional statement, perform corresponding operations, and switch the status. 2. Use the status transfer table.

In the first method, if the status is large, the conditional transfer part will be difficult to maintain, and it is not intuitive to use conditional statements to transfer the status. The second method simplifies the state transfer part compared with the first method, making it very convenient to implement and maintain state transfer. However, it only focuses on state transfer, it is difficult to add some necessary operations during the transfer process. It is also difficult to perform operations on Objects Based on the current status, and the status transfer is not intuitive enough. These two methods represent two extremes. One method makes it easier to add necessary operations during the State transfer process, but too many conditional statements are difficult to maintain, another method makes the implementation and maintenance of state transfer very convenient, but it is difficult to add additional operations during the transfer process. However, State pattern can cleverly eliminate the seemingly irreconcilable contradictions and absorb the advantages of the two methods. In state pattern, you only need to encapsulate the state to the object, and each time you give the required operation to the corresponding object, the object is automatically transferred after completion.

Specifically, for example, the TCP protocol contains three major states: established (creation), listening (listener), and closed (closed ). When the TCP connection is in these three different states, the operations are also different, and the transfer in different States is also different. To implement this function, you can simply think of using conditional statement judgment or status transfer tables. However, the disadvantages of the two are described earlier. If State pattern is used, you only need to create a tcpconnection object to represent the current connection. tcpestablished, tcplisten, and tcpclosed represent three State objects respectively, these three objects have the same parent class (tcpstate). tcpconnection has a tcpstate pointer. tcpconnection performs specific operations based on the specific subclass pointed to by the tcpstate pointer and performs specific state transfer. In this way, it is not only convenient for maintenance, but also intuitive for status transfer.

In state pattern, there is a context class that contains an abstract interface request and a State pointer. In addition, there is a state base class that contains an abstract interface handle, on the basis of this base class, you can also derive some specific subclass concretestate, which can implement the handle interface as needed. The context object can call the handle method of the State object to perform specific operations or state transfer based on the specific state object pointed by the State pointer.

The general structure of State pattern is as follows:



Strategy pattern:

Strategy Pattern defines a series of algorithms and encapsulates each of them so that they can exchange with each other. You can use strategy pattern to make the algorithm independent of its users.

Imagine a class and want to implement an operation (operatea) in different ways. there are usually two ways: 1. Define multiple different operation methods in the same class, then, when operatora is executed, you can determine the method used for execution. 2. Different subclasses are derived, and each subclass implements operatea based on different operation methods. Both of them can solve this problem, but they both have their own shortcomings: 1. the first method will make a class too large and difficult to maintain when there are many operation methods. 2. Method 2: although each class is not big, there are many classes and it is difficult to maintain them when there are many operation methods. 3. They also have some common disadvantages, so that the operation method (algorithm) is completely dependent on the class using the algorithm, and there is no independence, and when other classes want to use the same algorithm, it is very troublesome to redefine it.

The use of strategy pattern can solve this problem well: it extracts a type of operation methods (algorithms) and encapsulates them independently, and can be called through the same interface, every time you want to use an operation method, you only need to call it through the public interface of the algorithm, and the specific implementation of the algorithm has nothing to do with its users, making the algorithm independent of its users. At the same time, the same algorithm can be referenced by multiple different users (classes). In this way, when a user wants to expand an operation method (algorithm, you only need to add it in the algorithm class, instead of re-implementing it among every user who references the algorithm, which is very convenient.

In strategy pattern, there is a base class strategy, which provides an abstract interface algorithmintereface (). On the basis of strategy, you can define different sub-classes concretestrategy as needed. To use an algorithm, you only need to declare a strategy pointer. By pointing this strategy pointer to a specific subclass, you can call your own algorithminterface () through this subclass to complete the specific operation.

Usually, when the Code contains a lot of Continuous Condition Statement blocks, you need to consider using strategy pattern.

The general structure of strategy pattern is as follows:



Template Method pattern:

Template Method Pattern defines an algorithm framework in an operation, and submits some steps in this framework to the subclass for completion. Using template method pattern, let the subclass redefine some specific steps in the algorithm without changing the entire algorithm framework.

Sometimes, when writing a program, you will find that the structures of some different operations are roughly the same, but some specific steps are different. In this case, when writing, although there are many code differences between different operations, there is still a feeling of repeating the wheel. Template Method Pattern provides a code structure organization idea, which makes the code more reusable.

The key idea of template method pattern is to find out the common points and differences between different operation modules. The common points are organized and fixed into an algorithm framework using the template method. The difference points are extracted and encapsulated, and the differences between different operations have the same interface, perform different implementation on these differential vertex interfaces as needed, and finally call these interfaces in an organized and fixed algorithm framework. In this way, the implementation of some specific operations in the algorithm framework and framework is separated to make the two independent, which is conducive to code maintenance and reuse.

In template method pattern, a abstractclass class is defined first, which contains some abstract interfaces. One of the interfaces is used to organize, fix, and implement the algorithm framework ---- templatemethod (), other interfaces are declared for specific steps of the algorithm framework, such as primitiveoperation1 and primitiveoperation2. On the basis of the abstractclass class, you can also derive many specific subclasses. These subclasses can re-implement some specific step interfaces (such as primitiveoperation1 and primitiveoperation2) as needed, generally, you do not need to re-implement the templatemethod (Algorithm Framework). That is to say, the templatemethod is usually defined and fixed by the base class.

The general structure of template method pattern is as follows:



Visitor pattern:

Visitor pattern (visitor mode) represents an operation executed on an object structure element. With Visitor pattern, you can add some new operations without changing the element class of the structure object.

A node class derives multiple subnode classes. To perform some operations on a Node object, you can directly add a method to the node class, however, if it is implemented in a specific subclass, it cannot be called through the parent class pointer, which exposes the specific subclass and destroys encapsulation. If it is implemented in the parent class, different from the context (situation, context) of an object, a new method needs to be added. These methods may have nothing to do with some specific sub-classes, so that some sub-classes are contaminated, in addition, each time you add a new method, the entire class structure (parent class and all child classes of the parent class) will be re-compiled. If this operation is extracted and defined in a visitor class, you only need to define a sub-class of visitor to implement the operation based on context, the object class does not need to be re-compiled, and the class "pollution" does not occur, because the specific method is not in the node class at all. The Node class only provides one interface to receive node pointers, and call specific operations through the node pointer. This is the main idea of Visitor pattern.

However, the visitor class is usually designed based on the node class structure. If the node class structure changes, the visitor will also need a lot of modifications. When the node class changes frequently, visitor pattern does not apply. So the two key issues to consider when using visitor pattern are: Do the node class structures change frequently? If it changes frequently, the operation method should be defined in the node class. Do node classes often need to add new methods for some or all nodes based on different context? If yes, visitor pattern is a good choice.

In Visitor pattern, there are two levels: Visitor class hierarchy and element class hierarchy. In the visitor hierarchy, first define a Visitor Base class, which provides different abstract access (or operation) interfaces visitconcreteelementa and visitconcreteelementb based on different elements. Based on the visitor base class, different subclasses can also be derived. each subclass can implement the abstract access interfaces of different elements as needed. In the element hierarchy, first define an element base class, which defines an accept (visitor) interface, and element can be derived from different subclasses to represent different elements as needed, each specific element subclass can re-implement accept. When an element calls accept, it can call the corresponding operation using the passed visitor parameter object.

The general structure of Visitor pattern is as follows:



References:

[1] Erich Gamma, Richard Helm, Ralph Johnson, John vlissides. design patterns: Elements of reusable object oriented software. Pearson Education.

Design Pattern ---- behavioral patterns

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.