九折技术 九折技术
首页
  • Go
  • MIT-6824
  • 算法与数据结构
  • 面向对象
  • 代码整洁
  • 重构
  • 设计模式
  • 学习
  • 技术
  • 人文
关于
  • 网站
  • 资源
  • 分类
  • 标签
  • 归档
GitHub

HoldDie

长期有耐心,一切才刚刚开始!
首页
  • Go
  • MIT-6824
  • 算法与数据结构
  • 面向对象
  • 代码整洁
  • 重构
  • 设计模式
  • 学习
  • 技术
  • 人文
关于
  • 网站
  • 资源
  • 分类
  • 标签
  • 归档
GitHub
  • 代码整洁

  • 重构

  • 设计模式

    • 创建型-单例模式
    • 创建型-工厂模式
    • 创建型-建造者模式
    • 创建型-原型模式
    • 结构型-代理模式
    • 结构型-桥接模式
    • 结构型-装饰器模式
    • 结构型-适配器模式
    • 结构型-门面模式
    • 结构型-组合模式
    • 结构型-享元模式
    • 行为型-观察者模式
    • 行为型-模板模式
      • 行为型-策略模式
      • 行为型-责任链模式
      • 行为型-状态模式
      • 行为型-迭代器模式
      • 行为型-访问者模式
      • 行为型-备忘录模式
      • 行为型-命令模式
      • 行为型-解释器模式
      • 行为型-中介模式
    • 架构
    • 设计模式
    holddie
    2020-09-27

    行为型-模板模式

    # 模板模式

    • Template Method Design Pattern
    • 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。
    • 模板方法可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
    • 在 Java 中定义这样的模板方法,为了避免子类重写它,需要将函数定义为 final。

    # 模板方法作用

    • 复用
      • 模板模式把一个算法中不变的流程抽象到父类的模板方法中,将可变的部分留给子类来实现。
      • 所有的子类都可以复用父类中的模板方法定义的流程代码。
      • 主要通过继承父类代码来实现。
      • 实例
        • Java InputStream
          • 在 InputStream 读取数据中,有个 read() 函数是一个模板方法,定义了读取数据的整个流程,并且暴露了一个可以由子类来定制的抽象方法。
        • Java AbstractList
          • 在 AbstractList 类中,addAll() 函数可以看做模板方法,add() 是子类需要重写的方法。
          • 尽管没有声明为 abstract 的,但函数实现直接抛出了 UnsupportOperationException 异常。
    • 扩展
      • 这里的扩展,指的不是代码的扩展性,而是框架的扩展性,有点类似之前讲到的控制反转。
      • 实例
        • Java Servlet
          • 使用 Servlet 开发 Web 项目,我们需要定义一个继承 HttpServlet 的类,并且重写其中的 doGet() 或 doPost() 方法,来分别处理 get 和 post 请求。
          • HttpServlet 的 service() 方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,doGet()、doPost() 是模板中可以由子类来定制的部分。
          • Servlet 框架提供了一个扩展点(doGet()、doPost()方法),让框架用户在不用改变 Servlet 框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。
        • Junit TestCase
          • 在使用 JUnit 测试框架来编写单元测试的时候,我们编写的测试类都要继承框架提供的 TestCase 类。
          • 在 TestCase 类中,runBare() 函数是模板方法,它定义了执行测试用例的整体流程:先执行 setUp() 做些准备工作,然后执行 runTest() 运行真正的测试代码,最后执行 tearDown() 做扫尾工作。
          • TestCase 类的具体代码如下所示。尽管 setUp()、tearDown() 并不是抽象函数,还提供了默认的实现,不强制子类去重新实现,但这部分也是可以在子类中定制的,所以也符合模板模式的定义。

    # 回调函数

    # 定义

    • 相对于普通的函数调用来说,回调是一种双向调用关系。A 类事先注册某个函数 F 到 B 类,A 类在调用 B 类的 P 函数的时候,B 类反过来调用 A 类注册给它的 F 函数。
    • F 函数就是“回调函数”。A 调用 B,B 反过来又调用 A,这种调用机制就叫作“回调”。

    # 实例代码

    
    public interface ICallback {
      void methodToCallback();
    }
    
    public class BClass {
      public void process(ICallback callback) {
        //...
        callback.methodToCallback();
        //...
      }
    }
    
    public class AClass {
      public static void main(String[] args) {
        BClass b = new BClass();
        b.process(new ICallback() { //回调对象
          @Override
          public void methodToCallback() {
            System.out.println("Call back me.");
          }
        });
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    # 回调分类

    • 同步回调
      • 在函数返回之前执行回调函数。
    • 异步回调
      • 类似支付时,调用第三方接口,支持同步回调和异步回调。

    # 应用实例

    • JdbcTemplate
      • 尽管都叫作 xxxTemplate,但它们并非基于模板模式来实现的,而是基于回调来实现的,确切地说应该是同步回调。
      • Java 提供了 JDBC 类库来封装不同类型的数据库操作。不过,直接使用 JDBC 来编写操作数据库的代码,还是有点复杂的。
      • Spring 提供了 JdbcTemplate,对 JDBC 进一步封装,来简化数据库编程。
      • JdbcTemplate 通过回调的机制,将不变的执行流程抽离出来,放到模板方法 execute() 中,将可变的部分设计成回调 StatementCallback,由用户来定制。
      • query() 函数是对 execute() 函数的二次封装,让接口用起来更加方便。
    • Android setClickListener
      • 事件监听器很像回调,即传递一个包含回调函数(onClick())的对象给另一个函数。
      • 当用户点击按钮的时候,发送点击事件给观察者,并且执行相应的 onClick() 函数。
    • addShutdownHook
      • JVM 提供了 Runtime.addShutdownHook(Thread hook) 方法,可以注册一个 JVM 关闭的 Hook。当应用程序关闭的时候,JVM 会自动调用 Hook 代码。
      • 有关 Hook 的逻辑都被封装到 ApplicationShutdownHooks 类中了。当应用程序关闭的时候,JVM 会调用这个类的 runHooks() 方法,创建多个线程,并发地执行多个 Hook。

    # 模板模式 VS 回调

    • 从应用场景
      • 同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的。
      • 而异步回调跟模板模式有较大差别,更像是观察者模式。
    • 从代码实现
      • 回调基于组合关系来实现,把一个对象传递给另一个对象,是一种对象之间的关系;
      • 模板模式基于继承关系来实现,子类重写父类的抽象方法,是一种类之间的关系。

    # 回调相对于模板方法

    • 像 Java 这种只支持单继承的语言,基于模板模式编写的子类,已经继承了一个父类,不再具有继承的能力。
    • 回调可以使用匿名类来创建回调对象,可以不用事先定义类;而模板模式针对不同的实现都要定义不同的子类。
    • 如果某个类中定义了多个模板方法,每个方法都有对应的抽象方法,那即便我们只用到其中的一个模板方法,子类也必须实现所有的抽象方法。而回调就更加灵活,我们只需要往用到的模板方法中注入回调对象即可。
    编辑
    #模板模式
    上次更新: 2020/10/18, 14:10:00
    行为型-观察者模式
    行为型-策略模式

    ← 行为型-观察者模式 行为型-策略模式→

    最近更新
    01
    行为型-访问者模式
    11-24
    02
    行为型-备忘录模式
    11-24
    03
    行为型-命令模式
    11-24
    更多文章>
    Theme by Vdoing | Copyright © 2019-2020 HoldDie | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式