2023-03-27
原文作者:ClarenceZero 原文地址:https://blog.csdn.net/ClarenceZero/article/details/106887075

模板方法

定义

模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

(引用GoF 设计模式)

模板方法模型 是一种行为设计模型。 模板方法 是一个定义在父类别的方法,在 模板方法 中会呼叫多个定义在父类别的其他方法,而这些方法有可能只是抽象方法并没有实作, 模板方法 仅决定这些抽象方法的执行顺序,这些抽象方法的实作由子类别负责,并且子类别不允许覆写模板方法。

(引用维基百科)

类图

202303272245145471.png

实现

1. 定义抽象类

    package template;
    
    /**
     * 抽象类: 作为基类,其子类必须实现抽象方法
     */
    public abstract class CaffeineBeverage {
        /**
         * 整个流程处理
         * 声明为final以免子类改变这个算法的顺序
         */
        final void prepareRecipe() {
            // 把水煮沸
            boilWater();
            // 用热水泡咖啡或茶
            brew();
            // 把饮料倒进杯子
            pourInCup();
            // 在饮料内加入适当的调料
            addCondiments();
        }
    
        abstract void brew();
        abstract void addCondiments();
    
        void boilWater() {
            System.out.println("boil water. Both.");
        }
    
        void pourInCup() {
            System.out.println("pour in cup. Both");
        }
    
    }

2. 定义实现类

    public class Coffee extends CaffeineBeverage{
    
        @Override
        void brew() {
            System.out.println("Coffee brew.");
        }
    
        @Override
        void addCondiments() {
            System.out.println("Coffee add sugar and milk.");
        }
    }
    public class Tea extends CaffeineBeverage {
        @Override
        void brew() {
            System.out.println("Tea brew.");
        }
    
        @Override
        void addCondiments() {
            System.out.println("Tea add lemon.");
        }
    }

回调

代码

1. 定义回调接口

    public interface ICallback {
        void methodToCallback();
    }

2.定义B类(接收回调函数者)

    public class BClass {
        public void process(ICallback callback) {
            callback.methodToCallback();
        }
    }

3.定义A类(传递回调函数者)

    /**
     * 1.回调跟模板模式一样,也具有复用和扩展的功能.
     * 2.回调分为同步回调、异步回调(延迟回调)。
     *   同步回调指在函数返回之前执行回调函数、异步回调指的是在函数返回之后执行回调函数。
     * 3.从应用场景看,同步模式看起来更像模式模式,异步回调看起来更像观察者模式。
     */
    public class AClass {
        public static void main(String[] args) {
            BClass b = new BClass();
            b.process(new ICallback() {
                @Override
                public void methodToCallback() {
                    System.out.println("method call back.");
                }
            });
        }
    }

回调小结

  1. 回调跟模板模式一样,也具有复用和扩展的功能。A类可以根据实际情况自定义实现回调方法。有具大的操作空间。

  2. 相对于普通的函数调用来说,回调是一种双向调用关系。A类事先注册某个函数F到B类,A类在调用B类的P函数的时候,B类反过来调用A类注册给它的F函数。即A调用B,B返过来调用A

  3. 应用举例:

    1. JdbcTemplate
    2. addShundownHook()
  4. 模块模式VS回调

    从应用场景上来看,同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的。而异步回调跟模板模式有较大差别,更像是观察者模式。

    从代码实现上来看,回调和模板模式完全不同。回调基于组合关系来实现,把一个对象传递给另一个对象,是一种对象之间的关系;模板模式基于继承关系来实现,子类重写父类的抽象方法,是一种类之间的关系。

    组合优于继承。实际上,这里也不例外。在代码实现上,回调相对于模板模式会更加灵活,主要体现在下面几点。

    1. 像 Java 这种只支持单继承的语言,基于模板模式编写的子类,已经继承了一个父类,不再具有继承的能力。
    2. 回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类。
    3. 如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。

总结

  1. 模板模式主要用来解决复用扩展问题。

    • 复用

      把一个算法中不变的流程抽象到父类的模板方法 templateMethod()中,将可变的部分method1()、method2()留给子类 ContreteClass1ContreteClass2来实现。

      1. Java IO类库中,有很多设计用到了模板模式。如InputStream、OutputStream、Reader、Writer。在InputStream中,read()函数是一个模板方法,该方法定义了读取数据的整个流程,并且暴露了一个可以由子类定制的抽象方法。
      2. Java AbstractList类中。addAll()函数可以看作模板方法,虽然并未定义abstract抽象方法,但是函数直接抛出了异常。若要使用该方法,必须重写。
    • 扩展

      HttpServletservice()方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,doGet()、doPost()是模板中可以由子类来定制的部分。实际上,这就相当于Servlet框架提供了一个扩展点(doGet()、doPost() 方法),让框架用户在不用修改Servlet框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。

      又比如compareTo方法。让子类扩展该方法的实现。

  2. 模块模式基于继承。对Java单继承语言来说,会有较大限制。

  3. 文章中内容分别来自Head First 设计模式图解设计模式设计模式之美(极客学院)

阅读全文