设计模式(22):模板方法模式

尺有所短,寸有所长;不忘初心,方得始终。

一、模板方法模式是什么

程序设计中:设计一个系统时知道了算法所需的关键步骤,确定步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

【定义】:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

【主要作用】:将这些通用算法抽象出来,子类实现差异性行为,对多个行为方法进行编排。

【特性】:模板方法模式仅仅使用继承机制,它是一种类行为型模式。

二、模板方法模式的适用场景

  • 【适用场景】

    • 算法的整体骨架固定,其中个别算法的实现易变,可使用模板方法模式,将容易变算法抽象出来,供子类实现。
    • 多个子类存在公共的行为方法时,将其提取到一个公共父类中以避免代码重复。
    • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
    • 在代码重构时,经常会使用模板方法把相同的代码抽取到父类中,然后通过构造函数约束其行为。
  • 【生活案例】

    • 银行办理业务:取号、排队、具体业务,

      取号、排队的实现是一样的,可以在父类中实现,但是办理具体业务却可能是存款、取款或转账等,可以在子类中自行实现。

    • 我们每天会起床、吃饭、干活、睡觉

      起床、吃饭、睡觉的实现是一样的,可以在父类中实现,但是干活可能是上班,逛街等可以在子类中自行实现。

    • 一些文档的模板:简历模板、论文模板

      模板的格式实现是一样的,可以在父类中实现,但是正文有很多种,可以在子类中自行实现。

三、模板方法模式结构

  • 抽象模板类(Abstract Class)角色:定义算法的轮廓和骨架。由一个模板方法和若干个基本方法构成。

    ① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

    ② 基本方法:是整个算法中的一个步骤,包含以下几种类型。

    • 抽象方法:在抽象类中声明,由具体子类实现。

    • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。

    • 钩子方法:可以省略,用于判断的逻辑方法和需要子类重写的空方法两种。

      通过在具体子类中重写钩子方法来改变抽象父类中的运行结果

  • 具体实现(Concrete Class)角色

    实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。设计模式(22):模板方法模式

四、模板方法模式实现方式

  • 分析目标算法, 将其分解为多个步骤,将通用步骤与个性化步骤区分开。
  • 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。在模板方法中根据算法结构依次调用相应步骤。可用 final最终修饰模板方法以防止子类对其进行重写。
  • 在需要的时候可在算法的关键步骤之间添加钩子方法
  • 每个算法变体新建一个具体子类,实现所有的抽象步骤,并重写部分可选步骤。

五、模板方法模式的实现

【案例】:银行办理业务:取号、排队、具体业务,

【案例说明】:银行办理业务中取号、排队是固定的流程,可以由父类实现,而具体业务需要子类去实现。在父类中可以定义一个钩子方法,用户判断前面是否有人在办理业务,没人的时候可以不需要排队。

  • 抽象模板类(Abstract Class)角色

    /**
    * @describe 抽象模板类(Abstract Class)角色 银行业务
    * @author Edwin
    * @date 2021/11/22 14:48
    */

    public abstract class BankBusinessAbstract {

    /**
    * 模板方法:定义算法的骨架,对基本方法的调用顺序编排。
    * 定义成final类型,防止子类重写
    * @author Edwin
    * @date 2021/11/22 14:49
    */

    public final void bankTemplateMethod(){
    //取号
    takeNumber();
    if(isQueue()){
    // 前面有人,需要排队
    queue();
    }
    hook();
    //开始办理业务
    specificBusiness();
    }

    /**
    * 具体方法 :取号
    * @author Edwin
    * @date 2021/11/22 14:52
    */

    public void takeNumber(){
    System.out.println("已经取到号了");
    }
    /**
    * 具体方法 :排队
    * @author Edwin
    * @date 2021/11/22 14:52
    */

    public void queue(){
    System.out.println("前面还有好多人,排了好长的队");
    }

    /**
    * 具体方法 :具体业务,由子类实现
    * @author Edwin
    * @date 2021/11/22 14:52
    */

    public abstract void specificBusiness();

    /**
    * 钩子方法 :其他钩子空方法,交给子类实现
    * 模板方法模式中钩子方法可以省略,主要看业务需求
    * @author Edwin
    * @date 2021/11/22 14:55
    */

    public abstract void hook();
    /**
    * 钩子方法 :是否需要排队
    * 模板方法模式中钩子方法可以省略,主要看业务需求
    * @author Edwin
    * @date 2021/11/22 14:55
    */

    public boolean isQueue() {
    return false;
    }
    }
  • 具体实现(Concrete Class)角色

    /**
    * 具体实现(Concrete Class)角色 :实现抽象类中所定义的抽象方法和钩子方法
    * @author Edwin
    * @date 2021/11/22 15:06
    */

    public class BankBusinessConcrete extends BankBusinessAbstract {
    /**
    * 钩子方法 :子类重写的子空方法
    * @author Edwin
    * @date 2021/11/22 15:04
    */

    @Override
    public void hook() {
    System.out.println("边打游戏边排队,等了好久之后.....");
    }
    /**
    * 通过重写钩子方法,改变父类模板方法中的编排,改变程序的执行
    * @author Edwin
    * @date 2021/11/22 15:05
    */

    @Override
    public boolean isQueue() {
    return true;
    }
    @Override
    public void specificBusiness() {
    System.out.println("办理存款业务");
    }
    }

  • 客户端代码实现

    public static void main(String[] args) {
    BankBusinessConcrete concrete = new BankBusinessConcrete();
    concrete.bankTemplateMethod();
    }
  • 案例输出结果

设计模式(22):模板方法模式

六、模板方法模式的优缺点

  • 优点
    • 父类封装了不变部分,子类扩展可变部分,便于程序的扩展,提升代码复用。
    • 符合开闭原则,子类可以通过扩展方式增加相应的功能。
  • 缺点
    • 每个不同的实现都需要定义一个子类,增加类的个数,提高了系统的复杂度。
    • 无法避免继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

七、模板方法模式和其他模式的区别

  • 【工厂方法模式】是【模板方法模式】的一种特殊形式。工厂方法可以作为一个大型模板方法中的一个步骤。

  • 【模板方法模式】与【策略模式】在形式上类似,不同点在于:

    • 【模板方法模式】基于继承机制:允许你通过扩展子类中的部分内容来改变部分算法。作用在类层次, 是静态的。

    • 【策略模式】基于组合机制:可以通过对相应行为提供不同的策略来改变对象的部分行为。作用在对象层次, 允许在运行时切换行为。

八、总结

模板方法模式基于继承机制实现,子类通过重写部分父类方法,影响父类行为的结果,在实际的开发中使用的非常频繁,很多开源框架都会提供一个抽象类,当我们需要扩展功能时,只需要继承此抽象类,重写基本方法,调用模板方法即可。


原文始发于微信公众号(星河之码):设计模式(22):模板方法模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/27080.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!