Design Patterns - day04

5. Structural mode

5.6 Combination mode

5.6.1 Overview

 You must be very familiar with this picture. We can regard the above picture as a file system. We call this structure a tree structure. In the tree structure, the entire tree can be traversed by calling a certain method. When we find a leaf node, we can perform related operations on the leaf node. This tree can be understood as a large container, which contains many member objects, and these member objects can be container objects or leaf objects. However, due to the difference in function between container objects and leaf objects, we must distinguish between container objects and leaf objects during use, but this will bring unnecessary trouble to customers. As a customer, it always hopes to be able to Treat container objects and leaf objects consistently.

definition:

Also known as the partial whole pattern, it is used to treat a group of similar objects as a single object. The Composite pattern composes objects according to a tree structure, used to represent part and whole hierarchies. This type of design pattern is a structural pattern, which creates a tree structure of groups of objects.

5.6.2 Structure

Combination mode mainly includes three roles:

  • Abstract root node (Component): defines the common methods and properties of objects at all levels of the system, and can pre-define some default behaviors and properties.
  • Branch node (Composite): Define the behavior of the branch node, store child nodes, and combine the branch nodes and leaf nodes to form a tree structure.
  • Leaf node (Leaf): Leaf node object, there is no branch under it, and it is the smallest unit of system hierarchy traversal.

5.6.3 Case implementation

[Example] Software menu

As shown in the figure below, when we visit some other management systems, we can often see similar menus. A menu can contain menu items (menu items refer to menu items that no longer contain other content), and can also contain menus with other menu items, so it is appropriate to use the combination mode to describe the menu. Our requirement is for a menu. Prints out all the menus it contains, along with the names of the menu items.

 To implement this case, we first draw a class diagram:

Code:

Whether it is a menu or a menu item, it should inherit from a unified interface, and here we will call this unified interface a menu component.

//菜单组件  不管是菜单还是菜单项,都应该继承该类
public abstract class MenuComponent {

    protected String name;
    protected int level;

    //添加菜单
    public void add(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }

    //移除菜单
    public void remove(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }

    //获取指定的子菜单
    public MenuComponent getChild(int i){
        throw new UnsupportedOperationException();
    }

    //获取菜单名称
    public String getName(){
        return name;
    }

    public void print(){
        throw new UnsupportedOperationException();
    }
}

The MenuComponent here is defined as an abstract class, because there are some common properties and behaviors to be implemented in this class, and the Menu and MenuItem classes can only cover the methods they are interested in, without having to deal with unnecessary or uninteresting methods. For example Say, the Menu class can contain submenus, so you need to override the add(), remove(), getChild() methods, but MenuItem should not have these methods. The default implementation given here is to throw an exception, and you can also rewrite the default implementation according to your needs.

public class Menu extends MenuComponent {

    private List<MenuComponent> menuComponentList;

    public Menu(String name,int level){
        this.level = level;
        this.name = name;
        menuComponentList = new ArrayList<MenuComponent>();
    }

    @Override
    public void add(MenuComponent menuComponent) {
        menuComponentList.add(menuComponent);
    }

    @Override
    public void remove(MenuComponent menuComponent) {
        menuComponentList.remove(menuComponent);
    }

    @Override
    public MenuComponent getChild(int i) {
        return menuComponentList.get(i);
    }

    @Override
    public void print() {

        for (int i = 1; i < level; i++) {
            System.out.print("--");
        }
        System.out.println(name);
        for (MenuComponent menuComponent : menuComponentList) {
            menuComponent.print();
        }
    }
}

The Menu class has implemented all methods except the getName method, because the Menu class has the functions of adding menu, removing menu and getting submenu.

public class MenuItem extends MenuComponent {

    public MenuItem(String name,int level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public void print() {
        for (int i = 1; i < level; i++) {
            System.out.print("--");
        }
        System.out.println(name);
    }
}

MenuItem is a menu item and cannot have submenus, so the functions of adding menu, removing menu and getting submenu cannot be realized.

5.6.4 Classification of combined modes

When using the combination mode, according to the definition form of the abstract component class, we can divide the combination mode into two forms: transparent combination mode and safe combination mode.

  • Transparent Composite Mode

    In the transparent composition mode, all the methods used to manage member objects are declared in the abstract root node role, such as the , , and methods MenuComponentare declared in the example. The advantage of this is to ensure that all component classes have the same interface. The transparent composite mode is also the standard form of composite mode.addremovegetChild

    The disadvantage of the transparent combination mode is that it is not safe enough, because the leaf object and the container object are essentially different, and the leaf object cannot have objects at the next level, that is, it is impossible to contain member objects, so add(), remove () and other methods are meaningless, which will not cause errors during the compilation phase, but may cause errors if these methods are called during the runtime phase (if no corresponding error handling code is provided)

  • Security Composite Mode

    In the security composite mode, no method for managing member objects is declared in the abstract component role, but these methods are declared and implemented in the branch node Menuclass . The disadvantage of the safe composition mode is that it is not transparent enough, because the leaf component and the container component have different methods, and those methods used to manage member objects in the container component are not defined in the abstract component class, so the client cannot completely program against the abstraction, and must Treat leaf widgets and container widgets differently.

    ![](file://D:\work\data-ja va design pattern (illustration + framework source code analysis + actual combat) \Java design pattern data day04\notes\img\combination mode-security.png?msec=1680705231183 )

5.6.5 Advantages

  • Composite mode can clearly define hierarchical complex objects, expressing all or part of the object's hierarchy, which allows the client to ignore the differences in hierarchy, and facilitates the control of the entire hierarchy.
  • The client can use a composite structure or a single object in it consistently, regardless of whether it is dealing with a single object or the entire composite structure, which simplifies the client code.
  • It is very convenient to add new branch nodes and leaf nodes in the combination mode without any modification to the existing class library, which conforms to the "opening and closing principle".
  • Combination mode provides a flexible solution for the object-oriented realization of tree structure. Through the recursive combination of leaf nodes and branch nodes, a complex tree structure can be formed, but the control of the tree structure is very simple.

5.6.6 Usage Scenarios

The combination mode is born in response to the tree structure, so the usage scenario of the combination mode is where the tree structure appears. For example: file directory display, multi-level directory presentation and other tree structure data operations.

5.7 Flyweight mode

5.7.1 Overview

definition:

​ Use sharing technology to effectively support the reuse of a large number of fine-grained objects. It greatly reduces the number of objects that need to be created by sharing existing objects, and avoids the overhead of a large number of similar objects, thereby improving the utilization of system resources.

5.7.2 Structure

There are two states in Flyweight mode:

  1. Internal state, that is, a shareable part that does not change as the environment changes.
  2. External state refers to the non-shareable part that changes as the environment changes. The implementation essentials of Flyweight mode is to distinguish these two states in the application and externalize the external state.

The Flyweight mode mainly has the following roles:

  • Abstract flyweight role (Flyweight): usually an interface or an abstract class, which declares the public methods of the specific flyweight class in the abstract flyweight class. These methods can provide the internal data (internal state) of the flyweight object to the outside world, and at the same time External data (external state) can also be set via these methods.
  • Concrete Flyweight role: It implements the abstract flyweight class, called a flyweight object; it provides storage space for the internal state in the concrete flyweight class. Usually we can combine the singleton mode to design specific flyweight classes, and provide unique flyweight objects for each specific flyweight class.
  • Unsharable Flyweight role: Not all subclasses of the abstract flyweight class need to be shared, subclasses that cannot be shared can be designed as non-shared concrete flyweight classes; when a non-shared concrete flyweight class is required Objects can be created directly by instantiation.
  • Flyweight Factory role: responsible for creating and managing flyweight roles. When a customer object requests a flyweight object, the flyweight factory checks whether there is a flyweight object that meets the requirements in the system, and if it exists, it will be provided to the client; if it does not exist, a new flyweight object will be created.

5.7.3 Case implementation

[Example] Tetris

The picture below is a block in the well-known Tetris. If in the Tetris game, each different block is an instance object, these objects will take up a lot of memory space. The Flyweight mode is used below to implement .

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\Tetris.jpeg?msec=1680705231184)

First look at the class diagram:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\flyweight mode.png?msec=1680705231184)

code show as below:

Tetris has different shapes, and we can extract AbstractBox from these shapes to define common properties and behaviors.

public abstract class AbstractBox {
    public abstract String getShape();

    public void display(String color) {
        System.out.println("方块形状:" + this.getShape() + " 颜色:" + color);
    }
}

The next step is to define different shapes, such as IBox class, LBox class, OBox class, etc.

public class IBox extends AbstractBox {

    @Override
    public String getShape() {
        return "I";
    }
}

public class LBox extends AbstractBox {

    @Override
    public String getShape() {
        return "L";
    }
}

public class OBox extends AbstractBox {

    @Override
    public String getShape() {
        return "O";
    }
}

A factory class (BoxFactory) is provided to manage flyweight objects (that is, AbstractBox subclass objects). Only one factory class object is needed, so the singleton mode can be used. And provide the factory class with a method to get the shape.

public class BoxFactory {

    private static HashMap<String, AbstractBox> map;

    private BoxFactory() {
        map = new HashMap<String, AbstractBox>();
        AbstractBox iBox = new IBox();
        AbstractBox lBox = new LBox();
        AbstractBox oBox = new OBox();
        map.put("I", iBox);
        map.put("L", lBox);
        map.put("O", oBox);
    }

    public static final BoxFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final BoxFactory INSTANCE = new BoxFactory();
    }

    public AbstractBox getBox(String key) {
        return map.get(key);
    }
}

5.7.5 Advantages, disadvantages and usage scenarios

1. Advantages

  • Greatly reduce the number of similar or identical objects in memory, save system resources and improve system performance
  • The external state in Flyweight mode is relatively independent and does not affect the internal state

2. Disadvantages:

In order to make the object shareable, it is necessary to externalize part of the state of the flyweight object, separate the internal state and the external state, and complicate the program logic

3. Usage scenarios:

  • A system has a large number of identical or similar objects, resulting in a large amount of memory consumption.
  • Most of the state of an object can be externalized, and these external states can be passed into the object.
  • When using the Flyweight mode, it is necessary to maintain a Flyweight pool for storing the Flyweight objects, which consumes certain system resources. Therefore, it is worth using the Flyweight mode when the Flyweight objects need to be reused many times.

5.7.6 JDK source code analysis

The Integer class uses the Flyweight pattern. Let's look at the following example first:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = 127;
        Integer i2 = 127;

        System.out.println("i1和i2对象是否是同一个对象?" + (i1 == i2));

        Integer i3 = 128;
        Integer i4 = 128;

        System.out.println("i3和i4对象是否是同一个对象?" + (i3 == i4));
    }
}

Run the above code, the result is as follows:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\image-20200208212930857.png?msec=1680705231184)

Why does the first output statement output true and the second output statement output false? Decompile by decompiler software, the code is as follows:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)127);
        Integer i2 Integer.valueOf((int)127);
        System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
        Integer i3 = Integer.valueOf((int)128);
        Integer i4 = Integer.valueOf((int)128);
        System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
    }
}

As you can see from the above code, the bottom layer of the operation of directly assigning a value to a variable of the Integer type to a basic data type is used valueOf(), so you only need to look at this method

public final class Integer extends Number implements Comparable<Integer> {

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

You can see that Integerby default objects-128 ~ 127 between are first created and cached. When calling , if the parameter is between , the subscript is calculated and returned from the cache, otherwise a new object is created.IntegervalueOf-128 ~ 127Integer

6. Behavioral model

Behavioral patterns are used to describe the complex flow control of programs at runtime, that is, to describe how multiple classes or objects cooperate with each other to complete tasks that cannot be completed by a single object alone. 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 the inheritance mechanism to distribute behavior among classes, and the latter uses composition or aggregation to distribute behavior among objects. Since the combination relationship or aggregation relationship is less coupled than the inheritance relationship and satisfies the "principle of composite reuse", the object behavior pattern has greater flexibility than the class behavior pattern.

Behavioral patterns are divided into:

  • template method pattern
  • strategy pattern
  • command mode
  • Chain of Responsibility Pattern
  • state mode
  • Observer pattern
  • mediator pattern
  • iterator pattern
  • visitor pattern
  • memo mode
  • interpreter mode

Among the above 11 behavioral patterns, except for the template method pattern and the interpreter pattern which are class behavioral patterns, all others belong to the object behavioral pattern.

6.1 Template method pattern

6.1.1 Overview

In the process of object-oriented programming, programmers often encounter this situation: when designing a system, they know the key steps required by the algorithm and determine the execution order of these steps, but the specific implementation of some steps is still unknown. In other words, the implementation of certain steps is related to the specific environment.

For example, going to a bank to handle business generally goes through the following four processes: taking a number, queuing up, handling specific businesses, and rating bank staff, among which the business of taking a number, queuing up, and rating bank staff is important for each customer. The same can be implemented in the parent class, but the specific business is different from person to person. It may be deposits, withdrawals or transfers, etc., which can be delayed to subclasses.

definition:

Define the algorithm skeleton in an operation, and defer some steps of the algorithm to subclasses, so that subclasses can redefine some specific steps of the algorithm without changing the structure of the algorithm.

6.1.2 Structure

The Template Method pattern contains the following main roles:

  • Abstract Class: 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 the basic methods it contains in a certain order.

    • Basic method: It is the method to realize each step of the algorithm, and it is an integral part of the template method. There are three basic methods:

      • 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 a concrete class, and its subclasses can be overwritten or directly inherited.

      • Hook Method: It has been implemented in the abstract class, including logical methods for judgment and empty methods that need to be rewritten by subclasses.

        Generally, the hook method is a logical method for judging. The name of this type of method is generally isXxx, and the return value type is boolean.

  • Concrete Class: Implement the abstract methods and hook methods defined in the abstract class, which are the steps of a top-level logic.

6.1.3 Case Implementation

【Example】Stir-fry

The steps of cooking are fixed, divided into steps such as pouring oil, heating oil, pouring vegetables, pouring seasonings, and stir-frying. It is now simulated in code through the template method pattern. The class diagram is as follows:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\template method mode.png?msec=1680705231184)

code show as below:

public abstract class AbstractClass {

    public final void cookProcess() {
        //第一步:倒油
        this.pourOil();
        //第二步:热油
        this.heatOil();
        //第三步:倒蔬菜
        this.pourVegetable();
        //第四步:倒调味料
        this.pourSauce();
        //第五步:翻炒
        this.fry();
    }

    public void pourOil() {
        System.out.println("倒油");
    }

    //第二步:热油是一样的,所以直接实现
    public void heatOil() {
        System.out.println("热油");
    }

    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
    public abstract void pourVegetable();

    //第四步:倒调味料是不一样
    public abstract void pourSauce();


    //第五步:翻炒是一样的,所以直接实现
    public void fry(){
        System.out.println("炒啊炒啊炒到熟啊");
    }
}

public class ConcreteClass_BaoCai extends AbstractClass {

    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}

public class ConcreteClass_CaiXin extends AbstractClass {
    @Override
    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}

public class Client {
    public static void main(String[] args) {
        //炒手撕包菜
        ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
        baoCai.cookProcess();

        //炒蒜蓉菜心
        ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
        caiXin.cookProcess();
    }
}

Note: In order to prevent malicious operations, general template methods are added with the final keyword.

6.1.3 Advantages and disadvantages

advantage:

  • Improve code reusability

    Put the same part of the code in the abstract parent class, and put the different code in different subclasses.

  • reverse control

    Calling the operation of its subclasses through a parent class, and extending different behaviors through the specific implementation of the subclasses, realizes reverse control and conforms to the "opening and closing principle".

shortcoming:

  • For each different implementation, a subclass needs to be defined, which will lead to an increase in the number of classes, a larger system, and a more abstract design.
  • The abstract method in the parent class is implemented by the subclass, and the execution result of the subclass will affect the result of the parent class, which leads to a reverse control structure, which increases the difficulty of code reading.

6.1.4 Applicable scenarios

  • The overall steps of the algorithm are fixed, but when individual parts are volatile, the template method pattern can be used at this time to abstract the volatile parts for subclasses to implement.
  • It is necessary to determine whether a step in the parent class algorithm is executed through the subclass, so as to realize the reverse control of the subclass to the parent class.

6.1.5 JDK source code analysis

The InputStream class uses the template method pattern. Multiple read()methods , as follows:

public abstract class InputStream implements Closeable {
    //抽象方法,要求子类必须重写
    public abstract int read() throws IOException;

    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read(); //调用了无参的read方法,该方法是每次读取一个字节数据
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
}

As can be seen from the above code, read()the method is an abstract method, which must be implemented by subclasses. read(byte b[])The method calls read(byte b[], int off, int len)the method, so the method to focus on here is the method with three parameters.

In the 18th and 27th lines of this method, you can see that the abstract read()method .

The summary is as follows: The method of reading a byte array data has been defined in the InputStream parent class is to read one byte at a time, store it in the first index position of the array, and read len bytes of data . Specifically how to read a byte of data? Implemented by subclasses.

6.2 Strategy Pattern

6.2.1 Overview

Let’s look at the picture below first. There are many modes of travel for us to choose from. We can ride a bicycle, take a car, take a train, or take an airplane.

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\image-20200210143039168.png?msec=1680705231206)

As a programmer, you need to choose a development tool for development. Of course, there are many tools for code development. You can choose Idea for development, you can also use eclipse for development, and you can also use some other development tools.

![](file://D:\work\data-ja va design pattern (illustration + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\image-20200210144457478.png?msec=1680705231205)

definition:

​ This mode defines a series of algorithms and encapsulates each algorithm so that they can be replaced with each other, and changes in algorithms will not affect customers who use the algorithms. The strategy pattern belongs to the object behavior pattern. It encapsulates the algorithm, separates the responsibility of using the algorithm from the realization of the algorithm, and delegates to different objects to manage these algorithms.

6.2.2 Structure

The main roles of the Strategy pattern are as follows:

  • Abstract Strategy (Strategy) class: This is an abstract role, usually implemented by an interface or abstract class. This role gives all interfaces required by the concrete strategy classes.
  • Concrete Strategy (Concrete Strategy) class: implements the interface defined by the abstract strategy, and provides specific algorithm implementation or behavior.
  • Environment (Context) class: Holds a reference to a strategy class, which is eventually called by the client.

6.2.3 Case implementation

【Example】Promotion

A department store is having an annual sales promotion. Launch different promotional activities for different festivals (Spring Festival, Mid-Autumn Festival, Christmas), and the promoters will show the promotional activities to customers. The class diagram is as follows:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\strategy mode.png?msec=1680705231184)

code show as below:

Define a common interface for all promotions in a department store

public interface Strategy {
    void show();
}

Define specific strategic roles (Concrete Strategy): specific promotional activities for each festival

//为春节准备的促销活动A
public class StrategyA implements Strategy {

    public void show() {
        System.out.println("买一送一");
    }
}

//为中秋准备的促销活动B
public class StrategyB implements Strategy {

    public void show() {
        System.out.println("满200元减50元");
    }
}

//为圣诞准备的促销活动C
public class StrategyC implements Strategy {

    public void show() {
        System.out.println("满1000元加一元换购任意200元以下商品");
    }
}

Define the role of the environment (Context): used to connect the context, that is, to sell promotional activities to customers, here it can be understood as a salesperson

public class SalesMan {                        
    //持有抽象策略角色的引用                              
    private Strategy strategy;                 

    public SalesMan(Strategy strategy) {       
        this.strategy = strategy;              
    }                                          

    //向客户展示促销活动                                
    public void salesManShow(){                
        strategy.show();                       
    }                                          
}                                              

6.2.4 Advantages and disadvantages

1. Advantages:

  • Freely switch between strategy classes

    Since the strategy classes all implement the same interface, they can be switched freely.

  • easy to expand

    To add a new strategy, you only need to add a specific strategy class, and basically do not need to change the original code, which conforms to the "open and close principle"

  • Avoid using multiple conditional selection statements (if else), fully embody the object-oriented design idea.

2. Disadvantages:

  • Clients must be aware of all policy classes and decide for themselves which one to use.
  • The strategy pattern will generate many strategy classes, and the number of objects can be reduced to a certain extent by using the flyweight pattern.

6.2.5 Usage Scenarios

  • When a system needs to dynamically select one of several algorithms, each algorithm can be encapsulated into a strategy class.
  • A class defines multiple behaviors, and these behaviors appear in the form of multiple conditional statements in the operation of this class. Instead of these conditional statements, each conditional branch can be moved into its own strategy class.
  • 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 client.
  • When the system requires that clients using the algorithm should not know the data it operates on, the strategy pattern can be used to hide the data structures associated with the algorithm.
  • The only difference between multiple classes is that they behave differently. You can use the strategy mode to dynamically select the specific behavior to be executed at runtime.

6.2.6 JDK source code analysis

Comparatorstrategy pattern in . There is a sort()method , as follows:

public class Arrays{
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
}

Arrays is an environment role class. The sort method can pass a new strategy to make Arrays sort according to this strategy. For example, the following test class.

public class demo {
    public static void main(String[] args) {

        Integer[] data = {12, 2, 3, 2, 4, 5, 1};
        // 实现降序排序
        Arrays.sort(data, new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(Arrays.toString(data)); //[12, 5, 4, 3, 2, 2, 1]
    }
}

Here, when we call the sort method of Arrays, the second parameter is the sub-implementation class object of the Comparator interface. So Comparator acts as an abstract strategy role, while the specific sub-implementation class acts as a concrete strategy role. Ambient role classes (Arrays) should hold references to abstract policies to invoke. So, does the sort method of the Arrays class use compare()the method ? Let us continue to look at sort()the method , the code is as follows:

class TimSort<T> {
    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }
        ...
    }   

    private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {
        assert lo < hi;
        int runHi = lo + 1;
        if (runHi == hi)
            return 1;

        // Find end of run, and reverse range if descending
        if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
                runHi++;
            reverseRange(a, lo, runHi);
        } else {                              // Ascending
            while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
                runHi++;
        }

        return runHi - lo;
    }
}

The above code will eventually run into countRunAndMakeAscending()this method. We can see that only the compare method is used, so when calling the Arrays.sort method, only the class object of the specific compare override method is passed. This is also a method that must be implemented by subclasses in the Comparator interface.

6.3 Command Mode

6.3.1 Overview

In daily life, when we go out to eat, we will encounter the following scenes.

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\image-20200211130313251.png?msec=1680705231234)

definition:

Encapsulating a request as an object separates the responsibility for making the request from the responsibility for executing it. In this way, the two communicate through the command object, which is convenient for storing, transferring, calling, adding and managing the command object.

6.3.2 Structure

The command pattern consists of the following main roles:

  • Abstract command class (Command) role: Define the interface of the command and declare the method of execution.
  • Concrete Command (Concrete Command) role: a specific command that implements the command interface; usually holds the receiver and calls the function of the receiver to complete the operation to be performed by the command.
  • Realizer/Receiver (Receiver) role: Receiver, the object that actually executes the command. Any class may become a receiver, as long as it can realize the corresponding functions required by the order.
  • Invoker/Requester (Invoker) role: requires the command object to execute the request, 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 to say, it is equivalent to the entry of using the command object.

6.3.3 Case implementation

To implement the above case with code, we need to analyze who plays the role of command mode in this case.

Waiter: It is the role of the caller, who initiates the order.

Senior Chef: It is the role of the receiver, the person who actually executes the order.

Order: The order is included in the command.

The class diagram is as follows:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\command mode.png?msec=1680705231184)

code show as below:

public interface Command {
    void execute();//只需要定义一个统一的执行方法
}

public class OrderCommand implements Command {

    //持有接受者对象
    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() + "桌的订单:");
        Set<String> keys = order.getFoodDic().keySet();
        for (String key : keys) {
            receiver.makeFood(order.getFoodDic().get(key),key);
        }

        try {
            Thread.sleep(100);//停顿一下 模拟做饭的过程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println(order.getDiningTable() + "桌的饭弄好了");
    }
}

public class Order {
    // 餐桌号码
    private int diningTable;

    // 用来存储餐名并记录份数
    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);
    }
}

// 资深大厨类 是命令的Receiver
public class SeniorChef {

    public void makeFood(int num,String foodName) {
        System.out.println(num + "份" + foodName);
    }
}

public class Waitor {

    private ArrayList<Command> commands;//可以持有很多的命令对象

    public Waitor() {
        commands = new ArrayList();
    }

    public void setCommand(Command cmd){
        commands.add(cmd);
    }

    // 发出命令 喊 订单来了,厨师开始执行
    public void orderUp() {
        System.out.println("美女服务员:叮咚,大厨,新订单来了.......");
        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) {
        //创建2个order
        Order order1 = new Order();
        order1.setDiningTable(1);
        order1.getFoodDic().put("西红柿鸡蛋面",1);
        order1.getFoodDic().put("小杯可乐",2);

        Order order2 = new Order();
        order2.setDiningTable(3);
        order2.getFoodDic().put("尖椒肉丝盖饭",1);
        order2.getFoodDic().put("小杯雪碧",1);

        //创建接收者
        SeniorChef receiver=new SeniorChef();
        //将订单和接收者封装成命令对象
        OrderCommand cmd1 = new OrderCommand(receiver, order1);
        OrderCommand cmd2 = new OrderCommand(receiver, order2);
        //创建调用者 waitor
        Waitor invoker = new Waitor();
        invoker.setCommand(cmd1);
        invoker.setCommand(cmd2);

        //将订单带到柜台 并向厨师喊 订单来了
        invoker.orderUp();
    }
}

6.3.4 Advantages and disadvantages

1. Advantages:

  • Reduce system coupling. The command pattern decouples the object that invokes the operation from the object that implements the operation.
  • Adding or removing commands is very convenient. Using the command mode to add and delete commands will not affect other classes, it satisfies the "open and close principle", and is more flexible for expansion.
  • Macro commands can be implemented. The command mode can be combined with the combination mode to assemble multiple commands into a combined command, that is, a macro command.
  • It is convenient to implement Undo and Redo operations. The command mode can be combined with the memo mode described later to realize command revocation and restoration.

2. Disadvantages:

  • Using the command pattern may result in some systems having too many concrete command classes.
  • The system structure is more complicated.

6.3.5 Usage Scenarios

  • The system needs to decouple the request caller and request receiver so that the caller and receiver do not interact directly.
  • The system needs to specify requests, queue them, and execute them at different times.
  • The system needs to support the command's undo (Undo) operation and recovery (Redo) operation.

6.3.6 JDK source code analysis

Runable is a typical command mode. Runnable acts as a command, Thread acts as a caller, and the start method is its execution method.

//命令接口(抽象命令角色)
public interface Runnable {
    public abstract void run();
}

//调用者
public class Thread implements Runnable {
    private Runnable target;

    public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0();
}

It will call a native method start0(), call the system method, and start a thread. The receiver is open to programmers, who can define the receiver themselves.

/**
 * jdk Runnable 命令模式
 *        TurnOffThread : 属于具体
 */
public class TurnOffThread implements Runnable{
     private Receiver receiver;

     public TurnOffThread(Receiver receiver) {
         this.receiver = receiver;
     }
     public void run() {
         receiver.turnOFF();
     }
}
/**
 * 测试类
 */
public class Demo {
     public static void main(String[] args) {
         Receiver receiver = new Receiver();
         TurnOffThread turnOffThread = new TurnOffThread(receiver);
         Thread thread = new Thread(turnOffThread);
         thread.start();
     }
}

6.4 Chain of Responsibility Model

6.4.1 Overview

In real life, there are often such cases: a request can be processed by multiple objects, but the processing conditions or permissions of each object are different. For example, if a company employee asks for leave, the leaders who can approve the leave include the department head, deputy general manager, general manager, etc., but the number of days that each leader can approve is different. Employees must find different leaders to sign according to the number of days they want to ask for leave. That is to say, employees must remember information such as the name, phone number and address of each leader, which increases the difficulty. There are many other examples like this, such as seeking reimbursement for business trips from leaders, and the game of "drumming and passing flowers" in daily life.

definition:

Also known as the responsibility chain mode, in order to avoid coupling the request sender with multiple request handlers, all request handlers are connected into a chain by remembering the reference of the next object through the previous object; when a request occurs , the request can be passed down the chain until an object handles it.

6.4.2 Structure

The Chain of Responsibility pattern mainly includes the following roles:

  • Abstract handler (Handler) role: defines an interface for processing requests, including abstract processing methods and a subsequent connection.
  • Concrete Handler (Concrete Handler) role: implement the processing method of the abstract handler, judge whether the request can be processed, if it can handle the request, process it, otherwise transfer the request to its successor.
  • Client class (Client) role: Create a processing chain and submit a request to the specific processor object at the head of the chain. It does not care about the processing details and the delivery process of the request.

6.4.3 Case Implementation

Now it is necessary to develop a leave process control system. A leave of less than one day only needs the approval of the team leader; a leave of 1 to 3 days requires the approval of the department manager; a request of 3 to 7 days requires the approval of the general manager.

The class diagram is as follows:

![](file://D:\work\data-ja va design pattern (diagram + framework source code analysis + actual combat)\Java design pattern data day04\notes\img\responsibility chain mode.png?msec=1680705231205)

code show as below:

//请假条
public class LeaveRequest {
    private String name;//姓名
    private int num;//请假天数
    private String content;//请假内容

    public LeaveRequest(String name, int num, String content) {
        this.name = name;
        this.num = num;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public int getNum() {
        return num;
    }

    public String getContent() {
        return content;
    }
}

//处理者抽象类
public abstract class Handler {
    protected final static int NUM_ONE = 1;
    protected final static int NUM_THREE = 3;
    protected final static int NUM_SEVEN = 7;

    //该领导处理的请假天数区间
    private int numStart;
    private int numEnd;

    //领导上面还有领导
    private Handler nextHandler;

    //设置请假天数范围 上不封顶
    public Handler(int numStart) {
        this.numStart = numStart;
    }

    //设置请假天数范围
    public Handler(int numStart, int numEnd) {
        this.numStart = numStart;
        this.numEnd = numEnd;
    }

    //设置上级领导
    public void setNextHandler(Handler nextHandler){
        this.nextHandler = nextHandler;
    }

    //提交请假条
    public final void submit(LeaveRequest leave){
        if(0 == this.numStart){
            return;
        }

        //如果请假天数达到该领导者的处理要求
        if(leave.getNum() >= this.numStart){
            this.handleLeave(leave);

            //如果还有上级 并且请假天数超过了当前领导的处理范围
            if(null != this.nextHandler && leave.getNum() > numEnd){
                this.nextHandler.submit(leave);//继续提交
            } else {
                System.out.println("流程结束");
            }
        }
    }

    //各级领导处理请假条方法
    protected abstract void handleLeave(LeaveRequest leave);
}

//小组长
public class GroupLeader extends Handler {
    public GroupLeader() {
        //小组长处理1-3天的请假
        super(Handler.NUM_ONE, Handler.NUM_THREE);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("小组长审批:同意。");
    }
}

//部门经理
public class Manager extends Handler {
    public Manager() {
        //部门经理处理3-7天的请假
        super(Handler.NUM_THREE, Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("部门经理审批:同意。");
    }
}

//总经理
public class GeneralManager extends Handler {
    public GeneralManager() {
        //部门经理处理7天以上的请假
        super(Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
        System.out.println("总经理审批:同意。");
    }
}

//测试类
public class Client {
    public static void main(String[] args) {
        //请假条来一张
        LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");

        //各位领导
        GroupLeader groupLeader = new GroupLeader();
        Manager manager = new Manager();
        GeneralManager generalManager = new GeneralManager();

        groupLeader.setNextHandler(manager);//小组长的领导是部门经理
        manager.setNextHandler(generalManager);//部门经理的领导是总经理
        //之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。

        //提交申请
        groupLeader.submit(leave);
    }
}

6.4.4 Advantages and disadvantages

1. Advantages:

  • Reduced coupling between objects

    This pattern reduces the coupling between request sender and receiver.

  • Enhanced system scalability

    New request processing classes can be added as needed to meet the principle of opening and closing.

  • Enhanced flexibility in assigning responsibilities to objects

    When the workflow changes, you can dynamically change the members in the chain or modify their order, and you can also dynamically add or delete responsibilities.

  • Chain of Responsibility simplifies the connection between objects

    An object only needs to keep a reference pointing to its successor, and does not need to keep references to all other handlers, which avoids the use of numerous if or if···else statements.

  • responsibility sharing

    Each class only needs to handle the work that it should handle, and pass the unhandled ones to the next object to complete, clarify the scope of responsibility of each class, and conform to the single responsibility principle of the class.

2. Disadvantages:

  • There is no guarantee that every request will be processed. Since a request has no clear receiver, there is no guarantee that it will be processed, and the request may go all the way down the chain without being processed.
  • Compared with a long chain of responsibility, the processing of the request may involve multiple processing objects, and the system performance will be affected to a certain extent.
  • The rationality of the establishment of the chain of responsibility depends on the client, which increases the complexity of the client, and may cause system errors due to incorrect settings of the chain of responsibility, such as circular calls.

6.4.5 Source Code Analysis

In javaWeb application development, FilterChain is a typical application of the chain of responsibility (filter) pattern. The following is the simulation implementation analysis of Filter:

  • Simulate web request Request and web response Response

    public interface Request{
    
    }
    
    public interface Response{
    
    }
  • Analog web filter Filter

     public interface Filter {
         public void doFilter(Request req,Response res,FilterChain c);
     }
  • Simulate implementation of concrete filters

    public class FirstFilter implements Filter {
        @Override
        public void doFilter(Request request, Response response, FilterChain chain) {
    
            System.out.println("过滤器1 前置处理");
    
            // 先执行所有request再倒序执行所有response
            chain.doFilter(request, response);
    
            System.out.println("过滤器1 后置处理");
        }
    }
    
    public class SecondFilter  implements Filter {
        @Override
        public void doFilter(Request request, Response response, FilterChain chain) {
    
            System.out.println("过滤器2 前置处理");
    
            // 先执行所有request再倒序执行所有response
            chain.doFilter(request, response);
    
            System.out.println("过滤器2 后置处理");
        }
    }
  • Simulate the implementation of the filter chain FilterChain

    public class FilterChain {
    
        private List<Filter> filters = new ArrayList<Filter>();
    
        private int index = 0;
    
        // 链式调用
        public FilterChain addFilter(Filter filter) {
            this.filters.add(filter);
            return this;
        }
    
        public void doFilter(Request request, Response response) {
            if (index == filters.size()) {
                return;
            }
            Filter filter = filters.get(index);
            index++;
            filter.doFilter(request, response, this);
        }
    }
  • test class

    public class Client {
        public static void main(String[] args) {
            Request  req = null;
            Response res = null ;
    
            FilterChain filterChain = new FilterChain();
            filterChain.addFilter(new FirstFilter()).addFilter(new SecondFilter());
            filterChain.doFilter(req,res);
        }
    }

Guess you like

Origin blog.csdn.net/fgwynagi/article/details/129978905