4. Behavioral design patterns

  • Behavior patterns involve assigning responsibilities between algorithms and objects. Behavior patterns describe not only the patterns of objects or classes, but also the communication patterns between them. These patterns characterize complex control flows that are difficult to track at run time. They shift the user’s attention from the flow of control to the way objects are connected.
  • The behavior class pattern uses inheritance mechanisms to dispatch behavior between classes. This chapter includes two such patterns, of which TemplateMethnod is simple and commonly used. A template method is an abstract definition of an algorithm that defines the algorithm step by step, calling an abstract operation or a primitive operation at each step, and subclasses define the abstract operation to implement the algorithm concretely. Another behavior class pattern is Interpreter, which represents a grammar as a class hierarchy and implements an Interpreter as an operation on instances of those classes.
  • The behavior object pattern uses object composition instead of inheritance. Some behavior object patterns describe how a group of peer objects can work together to accomplish tasks that none of them can accomplish alone. An important issue here is the peer object.
  • How to get to know each other. Peers can keep explicit references to each other, but that increases their coupling. In extreme cases, each object needs to know about all the other objects. Mediator introduces a Mediator object between peers to avoid this situation. Mediators provide the indirection needed for loose coupling.
  • Chain of Responsibility provides looser coupling. It lets the user implicitly send requests to an object through a chain of candidate objects. Either candidate can respond to a request depending on the run-time situation. The number of candidates is arbitrary, and which candidates participate in the chain can be determined at run time.
  • The Observer pattern defines and maintains dependencies between objects. A typical Observer example is the model/view/controller in Smaltalk, where all views of the model are notified whenever the state of the model changes.
  • Other behavior object patterns often encapsulate behavior in an object and assign requests to it. The Strategy pattern encapsulates algorithms in objects, making it easy to specify and change the algorithm used by an object. The Command pattern encapsulates the request in an object so that it can be passed as a parameter, stored in a history list, or used in other ways. The State pattern encapsulates the State of an object so that the object can change its behavior when its State object changes. Visitor encapsulates behavior distributed across multiple classes, while Iterator abstracts the way objects in a collection are accessed and traversed.

4.1 Chain of Responsibility

  • 1) intent
    • This avoids coupling between the sender and receiver of the request by giving multiple objects the opportunity to process the request. Connect the objects into a chain and pass the request along the chain until an object processes it.
  • 2) structure
    • The structure diagram of the chain of responsibility mode is shown in the figure below.

    • Among them:
      • Handler defines an interface to handle requests; (Optional) Implement the successor chain.
      • Concretefamndler handles the request for which it is responsible; Accessible to its successors; If the request can be processed, it is processed, otherwise it is forwarded to a successor.
      • The Client submits a request to the ConcreteHandler object on the chain.
  • 3) Applicability
    • The Chain of Responsibility model applies to the following conditions:
      • Multiple objects can handle a request, and which object handles the request at runtime is automatically determined.
      • You want to submit a request to one of multiple objects without explicitly specifying the receiver.
      • The collection of objects that can handle a request should be specified dynamically.

4.2 Command

  • 1) intent
    • Encapsulating a request as an object allows the customer to be parameterized with different requests: queuing or logging requests, and supporting undoable operations.
  • 2) structure
    • The structure diagram of command mode is shown below.

  • Among them:
    • Command declares the interface on which the operation is performed.
    • ConcreteCommand Binds a receiver object to an action that invokes the corresponding action of the receiver to implement Execute.
    • Client creates a concrete command object and sets its receiver.
    • The Invoker asks the command to perform the request.
    • The Receiver knows how to perform the actions associated with executing a request. Any class can act as a receiver.
  • 3) Applicability
    • The Command mode applies to:
      • Parameterize an object by abstracting the action to be performed. The Command pattern is an object-oriented alternative to the callback (Calback) mechanism in procedural languages.
      • Specify, align, and execute requests at different times. A Command object can have a lifetime independent of the initial request. If the receiver of a request can be expressed in a way that is independent of the address space, the command object responsible for the request can be passed to a different process and the request implemented there.
      • Cancel operation is supported. The Execute operation of the Command stores the state before the operation is executed. The state is used to cancel the operation. The Command interface must add an Unexecute operation that cancels the effect of the last Execute call. Commands executed are stored in a history list. Unlimited “cancel” and “redo” multiplies can be implemented by traversing the list backwards and forwards and calling Unexecute and Execute respectively.
      • Supports log modification. This allows these changes to be redone in the event of a system crash. Add a load operation and a store operation to the Command interface that can be used to keep a consistent change log of changes. The recovery process from a crash involves re-reading recorded commands from disk and re-executing them with the Execute operation.
      • Construct a system with high-level operations built on top of primitive operations. Such a structure is common in information systems that support transactions (Transacion). The Command pattern provides a way to model transactions. Command has a common interface that makes it possible to invoke all transactions in the same way, and it is also easy to extend the system by adding new transactions.

4.3 Interpreter

  • 1) intent
    • Given a language, define a representation of its grammar, and define an interpreter that uses that representation to interpret sentences in the language.
  • 2) structure
    • The structure diagram of the interpreter pattern is shown below.
    • Among them:
      • AbstractExpression declares an interpreted operation of a program. This interface is shared by all nodes in the abstract syntax tree.
      • TerminalExpression implements an interpretive operation associated with a terminator ina grammar; Each terminator in a sentence requires an instance of the class.
      • NonterminalExpression Requires a NonterminalExpression class for every rule ina grammar; Maintain an instance variable of type AbstractExpression for each symbol; Implement Interpret operations for non-terminals in grammars.
      • Context contains some global information outside of the interpreter.
      • The Client builds (or is given) an abstract syntax tree that represents a particular sentence in the language defined by the method, assembled from NonterminalExpression and instances of TerminalExpression: invoke the explain operation.
  • 3) Applicability
    • The Interpreter pattern works best when there is a language that needs to be interpreted for execution and sentences in that language can be represented as an abstract syntax tree:
      • The method is simple. For complex publications, the class hierarchy of grammars becomes large and unmanageable. At this point the syntax parser
      • A tool like a sequence generator is a better choice. They can interpret expressions without building an abstract syntax tree, which saves space and possibly time.
      • Efficiency is not a key issue. The most efficient interpreters usually do not directly interpret parse trees, but first convert them into another form. However, even in this case, the converter can still be implemented in this mode.

4.Iterator

  • 1) intent
    • Provides a way to access elements in an aggregate object sequentially without exposing the internal representation of the object.
  • 2) structure
    • The structure of the iterator pattern is shown below.

  • Among them:
    • An lterator defines an interface for accessing and traversing elements.
    • Concretelterator Implements the iterator interface; Traverse the aggregation to track the current location.
    • Aggregate defines the interface for creating corresponding iterator objects.
    • ConcreteAggregate implements the interface that creates the corresponding iterator, which returns an appropriate instance of the Concretelterator.
  • 3) Applicability
    • The Iterator pattern applies to:
      • Access the content of an aggregate object without exposing its internal representation.
      • Supports multiple traversals of aggregate objects.
      • Provides a unified interface for traversing different aggregation structures.

4.5 Mediator

  • 1) intent
    • Set up a series of object interactions with a single object. A mediator enables objects to have a set of object interactions without explicitly referring to each other. The mediator loosens the coupling by eliminating the need for objects to explicitly refer to each other, and can change their interactions independently.
  • 2) structure
    • The structure diagram of the mediator pattern is shown in figure 1.

  • Among them:
    • A Mediator defines an interface for Colleague objects to communicate.
    • ConcreteMediator understands and maintains its colleagues by coordinating them for collaborative behavior.
    • A Colleague class knows its intermediary objects; Each colleague class object communicates with its mediator when it needs to communicate with other colleagues.
  • 3) Applicability
    • Mediator mode applies to:
      • When a set of objects communicate in a well-defined but complex way, the resulting interdependencies are unstructured and difficult to understand.
      • An object refers to many other objects and communicates directly with them, making it difficult to reuse the object.
      • You want to customize a behavior that is distributed across multiple classes without generating too many subclasses.

4.6 Memento

  • 1) intent
    • Capture the internal state of an object and store the state outside of the object without breaking encapsulation. This allows you to later restore the object to its original saved state.
  • 2) structure
    • The structure diagram of this pattern is shown below.

  • Among them:
    • Memento stores the internal state of the originator object. The originator decides what internal state of the originator the memo stores as needed; Prevent objects other than the originator from accessing the memo,
    • Originator Creates a memo recording its internal status at the current time. Use memos to restore internal state.
    • E.g. The Caretaker keeps the memo; The contents of this memo cannot be manipulated or examined.
  • 3) Applicability
    • The Memento pattern applies to:
      • You must save the (partial) state of an object at a certain point in time so that it can be restored to its previous state when needed later.
      • If one uses interfaces to make these states available to other objects directly, it exposes the implementation details of the object and breaks its encapsulation.

4. Observer

  • 1) intent
    • Defines a one-to-many dependency between objects in which all dependent objects are notified and automatically updated when an object’s state changes.
  • 2) structure
    • The structure of the observer mode is shown below.

  • Among them:
    • Subjiect knows its observers and can have any number of observers observing the same object: provides interfaces for registering and deleting observer objects.
    • The Observer defines an update interface for objects that need to be notified when the target changes.
    • Concrctesubiect stores the state in each ConcreteObserver object: notifies its observers when its state changes.
    • The ConcreteObserver maintains a reference to the ConcreteSubject object: stores states that correspond to the state of the target; implements the update interface for the Observer to match its state to the state of the target.
  • 3) the applicability
    • The Observer pattern applies to:
      • When an abstract model has two aspects, one of which depends on the other. Encapsulate the two in separate objects so that they can be changed and reused independently.
      • When changes to one object require changes to other objects without knowing exactly how many objects need to be changed.
      • When an object must inform other objects, and it cannot assume who the other objects are, it does not want those objects to be tightly coupled.

4.8 State

  • 1) intent
    • Allows an object to change its behavior when its internal state changes. Object appears to have modified its class.
  • 2) structure
    • The structure diagram of the state pattern is shown in figure 1.

  • Among them:
    • Context defines the interface of interest to the client, and maintains an instance of the Concretesate subclass that defines the current state.
    • State defines an interface to encapsulate behavior related to a particular State of the Context.
    • ConcreteState Means that each subclass implements behavior related to a state of the Context.
  • 3) Applicability
    • Sate mode applies to:
      • An object’s behavior is determined by its state, and it must change its behavior at runtime based on the state.
      • An operation contains a large number of conditional statements with multiple branches that depend on the state of the object. This state is often represented by one or more enumerated constants. Often, there are more than one operation that contains the same conditional structure. The State pattern puts each conditional branch into a separate class. This allows developers to treat the state of an object as an object, depending on the object itself, which can change independently of other objects.

4.9 Strategy

  • 1) intent
    • Define a set of algorithms, encapsulate them one by one, and make them interchangeable. This pattern allows algorithms to change independently of the customers who use them.
  • 2) structure
    • The structure diagram of the policy pattern is shown in figure 1.

  • Among them:
    • Stratgy (policy) defines the common interface for all supported algorithms. Contex uses this interface to call an algorithm defined by a ConcreteStrategy.
    • ConcreteStrategy Implements a concrete algorithm using the Srategy interface.
    • Contest(context) is configured with a ConcreteSrategy object; Maintain a reference to the Srategy object; You can define an interface that allows Strategy to access its data.
  • 3) Applicability
    • Strategy mode applies to:
      • Many of the related classes simply differ in behavior. Policy provides a way to configure a class with one of several behaviors.
      • You need to use different variations of an algorithm. For example, defining algorithms that reflect space/time trade-offs in different Spaces can use policy patterns when these variants are implemented as a class hierarchy of an algorithm.
      • Algorithms use data that customers shouldn’t know about. Policy patterns can be used to avoid exposing complex, algorithm-specific data structures.
      • A class defines multiple behaviors, and these behaviors occur in the form of multiple conditional statements in the class’s operations, which are replaced by the related conditional branches moved into their respective Strategy classes.

4.10 Template Method

  • 1) intent
    • Define an algorithm skeleton in an operation, deferring some steps to subclasses. Template Method allows subclasses to redefine specific steps of an algorithm without changing its structure.
  • 2) structure
    • The structure diagram of the template method pattern is shown below.

  • Among them:
    • AbstractClass defines abstract primitive operations that concrete subclasses will redefine to implement the steps of an algorithm; Implement template methods that specify the skeleton of an algorithm that calls not only primitive operations but also operations defined in AbstractClass or other objects.
    • The ConcreteClass implements primitive operations to complete steps in the algorithm related to a particular subclass.
  • 3) Applicability
    • The Template Method pattern applies to:
      • Implement the invariant parts of an algorithm once and leave the mutable behavior to subclasses.
      • Behaviors common to each subclass should be extracted and aggregated into a common parent class to avoid code duplication.
      • Control subclass extension. Template methods are designed to call “hook” operations (the default behavior, and subclasses can redefine extensions if necessary) at specific points, which only allow extensions at those points.

4.11 Visitor(Visitor)

  • 1) intent
    • Represents an operation that operates on elements in an object structure. It allows you to define new operations on elements without changing their classes.
  • 2) structure
    • The structure of the visitor pattern is shown in the figure below.

  • Among them:
    • Visitor declares a Visit operation for each class of the ConcreteElement in this object structure. The name and characteristics of the operation identify the class that sent the Visit request to the visitor, which allows the visitor to determine the specific class of the element being accessed. This allows visitors to access the element directly through its specific interface.
    • Concrete Visitor implements each operation declared by the Visitor, and each operation implements part of the algorithm, which is a class corresponding to an object in the structure. The ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates results as the structure is traversed.
    • Element defines an Accept operation that takes a visitor as an argument.
    • The ConcreteElement implements an Accept operation that takes a visitor as an argument.
    • An ObjectStructure can enumerate its elements; You can provide a high-level interface that allows that visitor to access its elements; It can be a combination or a collection, such as a list or an unordered collection.
  • 3) Applicability
    • Visitor pattern applies to:
      • An object structure contains many class objects with different interfaces, and users want to perform operations on these objects that depend on their concrete classes.
      • You need to perform many different and unrelated operations on objects in an object structure without “contaminating” their classes. Visitor allows users to group related operations into a class. When the object structure is shared by many applications, the Visitor pattern is used to allow each application to contain only the operations needed.
      • The class that defines the structure of an object rarely changes, but it is often necessary to define new operations on that structure. Changing the object structure class requires redefining the interface to all visitors, which can be costly. If object structure classes change frequently, it might be better to define these operations in those classes.

4.12 Comparison of behavior patterns

  • Many behavioral patterns focus on encapsulating change. These patterns define an object that encapsulates an aspect of a program when its characteristics change frequently. This way, when other parts of the program depend on this aspect, they can all work with this object. These schemas usually define an abstract class to describe the objects that encapsulate the changes, and usually the schema is named after this object. Such as:
    • A Strategy object encapsulates an algorithm;
    • A State object encapsulates a state-related behavior;
    • A Mediator object encapsulates a protocol between objects;
    • An Iterator encapsulates methods that access and traverse the components of an aggregate object.
  • These patterns describe aspects of the program that are likely to change. Most schemas have two kinds of objects: objects that encapsulate the characteristics of that aspect and existing objects that use these new objects. However, not all object behavior modes have such segmentation function. For example, Chain of Responsibility can deal with any number of objects (i.e. a Chain), and all of these objects may already exist in the system. This illustrates another difference in behavior patterns: Not all behavior patterns define static communication relationships between classes.
  • Some patterns introduce objects that are always used as arguments. For example, a Visitor object is an argument to a polymorphic Accept operation that operates on the object that the Visitor object visits. Other patterns define objects that can be passed as tokens that will be invoked later. For example, Command and Memento. In Command, a token represents a request; In Memento, two represent the internal state of an object at a given moment. In both cases, the token can have a complex internal representation, but the customer is not aware of it. Also, polymorphism is particularly important in Command mode because executing a Command object is a polymorphic operation. The Memento interface is so small that a memo can only be passed as a value, so it probably doesn’t provide any polymorphic operations to its clients at all.
  • Mediator and Observer are competing modes. The difference between them is that the Observer distributes communication by introducing Observer and Subject objects, while the Mediator object encapsulates communication between other objects. There is no single object that encapsulates a constraint in the Observer pattern; instead, the Observer and Subject object must collaborate to maintain the constraint. The communication pattern is determined by how the Observer and Subject are connected: a Subject usually has multiple observers, and sometimes one Subject’s Observer is also the target of another Observer. The Observer pattern facilitates separation and loose coupling between the Observer and the Subject, making it easier to produce finer grained and more reusable classes. The Mediator pattern is intended to be centralized rather than distributed, placing the responsibility for maintaining a constraint directly in an intermediary.
  • Command, Observer, Mediator and Chain of Responsibility all involve how to decouple sender and receiver, but they have different tradeoffs.
  • The Command pattern uses a Command object to define a binding relationship between a sender and a receiver, and supports decoupling. The Command object provides a simple interface to submit a request (Execute operation). The connection between sender and receiver is defined in one object, which enables the sender to work with different receivers. This decouples the sender from the receiver and makes the sender easier to reuse. In addition, you can reuse Command objects to parameterize a receiver with different senders.
  • The Observer pattern decouples the sender (the target) from the receiver (the Observer) by defining an interface to notify the target of changes that have occurred. The Observer defines a looser send-receiver binding than Command, because a target can have multiple observers and the number can vary at run time. The Subject and Observer interfaces in this pattern are designed to handle changes in subjects, so when objects have data dependencies, this pattern is best used to decouple them.
  • The Mediator pattern decouples objects by having them refer to each other indirectly through a Mediator object. A Mediator object routes requests between Colleague objects and centralizes their communication. Therefore, Colleague objects can communicate with each other only through the Mediator interface. The Mediator interface is fixed, and in order to increase flexibility, Mediator may have to implement its own distribution strategy. Requests can be encoded and parameters can be packaged in such a way that a Colleague object can request an unlimited number of operations. Because this pattern centralizes communication behavior into a class rather than spreading it across subclasses, it can reduce subclass generation for a system.
  • The Chain of Responsibility pattern decouples senders and receivers by passing requests along a Chain of potential receivers. Because the interface between sender and receiver is fixed, the chain of responsibility may also require a custom distribution strategy.