Behavioral model

1, Introduction

Behavioral pattern is used to describe the complex process control of a program at run time, that is, how multiple classes or objects cooperate with each other to complete tasks that can not be completed by a single object. It involves the allocation of responsibilities between algorithms and objects. Behavioral patterns are divided into class behavior patterns and object behavior patterns. The former uses inheritance mechanism to allocate behavior between classes, and the latter uses combination or aggregation to allocate behavior between objects. Because the coupling degree of composite relationship or aggregation relationship is lower than that of inheritance relationship and meets the "composite Reuse Principle", the object behavior pattern has more flexibility than the class behavior pattern.

Specific classification: template method mode, policy mode, command mode, responsibility chain mode, status mode, observer mode, mediator mode, iterator mode, visitor mode, memo mode and interpreter mode

Except that the template method pattern and Interpreter pattern are class behavior patterns, all the others belong to object behavior patterns.

Two, template method mode

Definition: define the algorithm skeleton in an operation, and delay some steps of the algorithm to the subclass, so that the subclass can redefine some specific steps of the algorithm without changing the structure of the algorithm.

Structure:

Abstract Class: it is responsible for giving the outline and skeleton of an algorithm. It consists of a template method and several basic methods.

Template method: defines the skeleton of the algorithm and calls its basic methods in a certain order.

Basic method: it is the method to realize each step of the algorithm and an integral part of the template method. The basic methods can be divided into three types:

Abstract method: an abstract method is declared by an abstract class and implemented by its concrete subclasses.

Concrete method: a concrete method is declared and implemented by an abstract class or concrete class, and its subclasses can be overridden or inherited directly.

Hook method: it has been implemented in abstract classes, including logical methods for judgment and empty methods that need to be overridden by subclasses. General hook methods are logical methods used for judgment. The name of such methods is usually isXxx, and the return value type is boolean.

Concrete Class: implement the abstract methods and hook methods defined in the abstract class. They are the constituent steps of top-level logic.

Case analysis: the steps of cooking are fixed, including pouring oil, hot oil, pouring vegetables, pouring spices, stir frying and so on. Now use the template method pattern to simulate with code. Class diagram is as follows:

abstract class

public abstract class AbstractClass {
    
    public final void cookProcess() {
        //Step 1: pour oil
        this.pourOil();
        //Step 2: hot oil
        this.heatOil();
        //Step 3: pour the vegetables
        this.pourVegetable();
        //Step 4: pour the seasoning
        this.pourSauce();
        //Step 5: stir fry
        this.fry();
    }
​
    public void pourOil() {
        System.out.println("Pour oil");
    }
​
    //Step 2: the hot oil is the same, so it can be realized directly
    public void heatOil() {
        System.out.println("hot oil");
    }
​
    //Step 3: pouring vegetables is different (one is cabbage, the other is cabbage)
    public abstract void pourVegetable();
​
    //Step 4: pouring seasoning is different
    public abstract void pourSauce();
​
​
    //Step 5: stir fry is the same, so it can be realized directly
    public void fry(){
        System.out.println("Fry and fry until cooked");
    }
}

Specific subclass

public class ConcreteClass_BaoCai extends AbstractClass {
​
    @Override
    public void pourVegetable() {
        System.out.println("The vegetables in the pot are cabbage");
    }
​
    @Override
    public void pourSauce() {
        System.out.println("The sauce in the pot is pepper");
    }
}
​
public class ConcreteClass_CaiXin extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("The vegetables in the pot are heart vegetables");
    }
​
    @Override
    public void pourSauce() {
        System.out.println("The sauce is minced garlic");
    }
}

Test class

public class Client {
    public static void main(String[] args) {
        //Fried shredded cabbage
        ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
        baoCai.cookProcess();
​
        //Stir fried cabbage with minced garlic
        ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
        caiXin.cookProcess();
    }
}

Note: in order to prevent malicious operations, the general template method adds the final keyword.

advantage

Improve code reusability, put the same part of the code in the abstract parent class, and put different code in different subclasses.
It realizes the reverse control, calls the operation of its subclasses through a parent class, extends different behaviors through the specific implementation of subclasses, realizes the reverse control, and conforms to the "opening and closing principle".

shortcoming

A subclass needs to be defined for each different implementation, which will increase the number of classes, make the system larger and the design more abstract.
Abstract methods in the parent class are implemented by subclasses, and the execution results of subclasses will affect the results of the parent class, which leads to a reverse control structure, which improves the difficulty of code reading.

Usage scenario

The overall steps of the algorithm are very fixed, but when individual parts are changeable, you can use the template method pattern to abstract the easily changeable parts for subclass implementation.
It is necessary to determine whether a step in the parent algorithm is executed through the subclass to realize the reverse control of the subclass over the parent.

3, Strategy mode

Definition: this pattern 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 using the algorithm. The policy pattern belongs to the object behavior pattern. It separates the responsibility of using the algorithm from the implementation of the algorithm by encapsulating the algorithm, and delegates it to different objects to manage these algorithms.

Structure:

Abstract Strategy class: This is an abstract role, which is usually implemented by an interface or abstract class. This role gives the interfaces required by all specific policy classes.
Concrete Strategy class: implements the interface of abstract policy definition and provides specific algorithm implementation or behavior.
Context class: holds a reference to a policy class, which is finally called to the client.

Case analysis: a department store's annual promotional activities. Different promotional activities are launched for different festivals (Spring Festival, Mid Autumn Festival and Christmas), and the promoters show the promotional activities to customers. Class diagram is as follows:

Abstract strategy

public interface Strategy {
    void show();
}

Specific strategies

//Promotional activities for the Spring Festival A
public class StrategyA implements Strategy {
​
    public void show() {
        System.out.println("Buy 1 Get 1 FREE");
    }
}
​
//Promotional activities for the Mid Autumn Festival B
public class StrategyB implements Strategy {
​
    public void show() {
        System.out.println("50 yuan less than 200 yuan");
    }
}
​
//Promotional activities for Christmas C
public class StrategyC implements Strategy {
​
    public void show() {
        System.out.println("Over 1000 yuan plus 1 yuan for any commodity below 200 yuan");
    }
}

Environment class

public class SalesMan {                        
    //Holds a reference to an abstract policy role                              
    private Strategy strategy;                 
                                               
    public SalesMan(Strategy strategy) {       
        this.strategy = strategy;              
    }                                          
                                               
    //Show promotions to customers                                
    public void salesManShow(){                
        strategy.show();                       
    }                                          
} 

advantage

Policy classes can be switched freely. Since policy classes all implement the same interface, they can be switched freely.
It is easy to expand. To add a new policy, you only need to add a specific policy class. There is basically no need to change the original code, which conforms to the "open close principle"“
Avoid using multiple conditional selection statements (if else) and fully reflect the idea of object-oriented design.

shortcoming

The client must know all policy classes and decide which policy class to use.
The policy pattern will result in many policy classes. You can reduce the number of objects to a certain extent by using the meta pattern.

Usage scenario

When a system needs to dynamically select one of several algorithms, each algorithm can be encapsulated into a policy class.
A class defines multiple behaviors, and these behaviors appear in the form of multiple conditional statements in the operation of this class. Each conditional branch can be moved into their respective policy class to replace these conditional statements.
Each algorithm in the system is completely independent of each other, and it is required to hide the implementation details of the specific algorithm from the customer.
When the system requires that the customer using the algorithm should not know the data it operates, the policy pattern can be used to hide the data structure related to the algorithm.
Multiple classes only have different behaviors. You can use the policy mode to dynamically select the specific behavior to be executed at run time.

4, Command mode

Definition: encapsulate a request as an object, separating the responsibility of sending 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.

Structure:

Abstract Command class (Command) role: defines the interface of the Command and declares the method to be executed.
Concrete Command role: a specific command that implements the command interface; Usually, the receiver will be held and the receiver's function will be called to complete the operation to be performed by the command.
Implementer / Receiver role: Receiver, the object that actually executes the command. Any class can become a Receiver as long as it can implement the corresponding functions required by the command.
Caller / requester role: it requires the command object to execute the request. It usually holds the command object, and can hold many command objects. This is where the client actually triggers the command and requires the command to perform the corresponding operation, that is, it is equivalent to using the entry of the command object.

Case analysis: the customer asks the waiter to order, and the waiter gives the order to the chef.

Attendant: it's the caller role. She initiates the command.
Senior Chef: it is the role of receiver, the real object of command execution.
Order: the order is included in the command.

 

 

public interface Command {
    void execute();//You only need to define a unified execution method
}
​
public class OrderCommand implements Command {
​
    //Holding recipient object
    private SeniorChef receiver;
    private Order order;
​
    public OrderCommand(SeniorChef receiver, Order order){
        this.receiver = receiver;
        this.order = order;
    }
​
    public void execute()  {
        System.out.println(order.getDiningTable() + "Table order:");
        Set<String> keys = order.getFoodDic().keySet();
        for (String key : keys) {
            receiver.makeFood(order.getFoodDic().get(key),key);
        }
​
        try {
            Thread.sleep(100);//Pause to simulate the cooking process
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
​
        System.out.println(order.getDiningTable() + "The table is ready");
    }
}
​
public class Order {
    // Table number
    private int diningTable;
​
    // It is used to store the meal name and record the number of copies
    private Map<String, Integer> foodDic = new HashMap<String, Integer>();
​
    public int getDiningTable() {
        return diningTable;
    }
​
    public void setDiningTable(int diningTable) {
        this.diningTable = diningTable;
    }
​
    public Map<String, Integer> getFoodDic() {
        return foodDic;
    }
​
    public void setFoodDic(String name, int num) {
        foodDic.put(name,num);
    }
}
​
// Senior chefs are ordered Receiver
public class SeniorChef {
​
    public void makeFood(int num,String foodName) {
        System.out.println(num + "share" + foodName);
    }
}
​
public class Waitor {
​
    private ArrayList<Command> commands;//You can hold many command objects
public Waitor() {
        commands = new ArrayList();
    }
    
    public void setCommand(Command cmd){
        commands.add(cmd);
    }
​
    // The order came and the cook began to carry it out
    public void orderUp() {
        System.out.println("Beauty waiter: Ding Dong, chef, here comes the new order.......");
        for (int i = 0; i < commands.size(); i++) {
            Command cmd = commands.get(i);
            if (cmd != null) {
                cmd.execute();
            }
        }
    }
}
​
public class Client {
    public static void main(String[] args) {
        //Create 2 order
        Order order1 = new Order();
        order1.setDiningTable(1);
        order1.getFoodDic().put("Tomato and egg noodles",1);
        order1.getFoodDic().put("Small coke",2);
​
        Order order2 = new Order();
        order2.setDiningTable(3);
        order2.getFoodDic().put("Rice with shredded pork and pepper",1);
        order2.getFoodDic().put("Small sprite",1);
​
        //Create recipient
        SeniorChef receiver=new SeniorChef();
        //Encapsulate orders and recipients into command objects
        OrderCommand cmd1 = new OrderCommand(receiver, order1);
        OrderCommand cmd2 = new OrderCommand(receiver, order2);
        //Create caller waitor
        Waitor invoker = new Waitor();
        invoker.setCommand(cmd1);
        invoker.setCommand(cmd2);
​
        //Take the order to the counter and call the cook for the order
        invoker.orderUp();
    }
}

advantage:

Reduce the coupling of the system. Command mode can decouple the object that invokes the operation from the object that implements the operation.
Adding or deleting commands is very convenient. Using the command mode, adding and deleting commands will not affect other classes. It meets the "opening and closing principle" and is flexible for expansion.
Macro commands can be realized. The command mode can be combined with the combined mode to assemble multiple commands into a combined command, that is, macro commands.
It is convenient to realize Undo and Redo operations. The command mode can be combined with the memo mode described later to realize command revocation and recovery.

Disadvantages:

Using command mode may cause some systems to have too many specific command classes.
The system structure is more complex.

Usage scenario

The system needs to decouple the request caller and the request receiver so that the caller and the receiver do not interact directly.
The system needs to specify, queue and execute requests at different times.
The system needs to support Undo and redo operations of commands.

For detailed teaching, please move to: https://www.bilibili.com/video/BV1Np4y1z7BU

  

 

Tags: Design Pattern

Posted on Thu, 02 Dec 2021 13:51:47 -0500 by mtwildcard