Design Patterns_Behavioral Patterns - "Template Method Patterns"

Design Patterns_Behavioral Patterns - "Template Method Patterns"

The notes are organized from the detailed explanation of Java design patterns by dark horse programmers, 23 Java design patterns (diagram + framework source code analysis + actual combat)

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 类行为模式and 对象行为模式, 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 model
  • state mode
  • Observer pattern
  • mediator pattern
  • iterator pattern
  • visitor pattern
  • memo mode
  • interpreter mode

The above 11 behavioral patterns, 除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式.

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, handling specific businesses, and rating bank staff, among which the business of taking a number, queuing, 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.

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.
        (The general 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 type)
  • Concrete Class: Implement the abstract methods and hook methods defined in the abstract class, which are the steps of a top-level logic.

Case realization

【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:

code show as below:

  • Abstract class (defining template methods and base methods)

    public abstract class AbstractClass {
          
          
        
        // 模板方法(声明为final 禁止子类修改)
        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("炒啊炒啊炒到熟啊");
        }
    }
    
  • Concrete subclass

    // 蔬菜类-包菜
    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("下锅的酱料是蒜蓉");
        }
    }
    
  • test class

    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();
        }
    }
    

    output

    倒油
    热油
    下锅的蔬菜是包菜
    下锅的酱料是辣椒
    炒啊炒啊炒到熟啊
    倒油
    热油
    下锅的蔬菜是菜心
    下锅的酱料是蒜蓉
    炒啊炒啊炒到熟啊
    

Note: In order to prevent malicious operations, finalkeywords are added to general template methods.

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.

Applicable scene

  • 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.

JDK source code analysis-InputStream

The InputStream class uses the template method pattern. Multiple methods are defined in the InputStream class read(), 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方法(也就是子类的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, the method without parameters read()is an abstract method, which must be implemented by subclasses. The method read(byte b[])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 without parameters is called.

InputStream is an abstract class, and the method with three parameters read(byte b[], int off, int len)is a template method, which defines the framework of the algorithm, calls read()the method without parameters multiple times and stores the byte data obtained each time into an array. Therefore, read()the method is an abstract method, and the subclass must implement it.

Summarize:

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.

Template method design in AQS

Reference Hao Ge

One of the typical applications of template method design pattern in Java is the design of AQS AQS就是一个抽象类,我们在自定义一些同步器时需要继承AQS(比如ReentrantLock等)并实现共享资源state如何获取和释放,至于具体线程入队阻塞以及如何被唤醒出队的逻辑,AQS已经帮我们实现好了.

  • Example: Look at an AQS template methodacquire()

    // 1.acquire()方法
    /*
     * tryAcquire() 留给子类实现
     * acquireQueued() AQS已经实现好了,即获取同步状态失败时的入队阻塞逻辑。
     */
    public final void acquire(int arg) {
          
          
       if (!tryAcquire(arg) &&
           acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
           selfInterrupt();
    }
    
    // AQS.tryAcquire() 没有设计成抽象方法,而是直接抛出异常,具体是让子类实现如何获取同步状态。
    protected boolean tryAcquire(int arg) {
          
          
        throw new UnsupportedOperationException();
    }
    
  • Take a look at how ReentrantLock does it

    // ReentrantLock内部定义一个内部类Sync实现AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {
          
          
        
        // 重写了AQS留给我们的tryAcquire()
        protected final boolean tryAcquire(int acquires) {
          
          
              // 具体的实现 ...
        }
    }
    
  • A method reserved for subclass custom implementation in the template method of AQS

    // 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS操作设置同步状态
    protected boolean tryAcquire(int arg);
    
    // 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态
    protected boolean tryRelease(int arg) 
        
    // 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之获取失败
    protected int tryAcquireShared(int arg)   
    
    // 共享式释放同步状态
    protected boolean tryReleaseShared(int arg)    
    
    // 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占。    
    protected boolean isHeldExclusively()    
    

Guess you like

Origin blog.csdn.net/weixin_53407527/article/details/128627959