行为型模式 - 模板模式(Template Pattern)

简介: 行为型模式 - 模板模式(Template Pattern)

前言

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。

行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式分为:

  • 模板方法模式
  • 策略模式
  • 命令模式
  • 职责链模式
  • 状态模式
  • 观察者模式
  • 中介者模式
  • 迭代器模式
  • 访问者模式
  • 备忘录模式
  • 解释器模式

以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。

一、 模板模式概述

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

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

二、模板模式结构

模板方法(Template Method)模式包含以下主要角色:

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
  • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
  • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
  • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
  • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
  • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

三、 模板模式案例实现

【例】炒菜 炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:

网络异常,图片无法展示
|
代码如下:

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

注意:为防止恶意操作,一般模板方法都加上 final 关键词。

四、 模板模式优缺点

优点:

  • 提高代码复用性将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。
  • 实现了反向控制通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。缺点:
  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

五、 模板模式适用场景

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

六、JDK源码解析

InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read() 方法,如下:

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

从上面代码可以看到,无参的 read() 方法是抽象方法,要求子类必须实现。而 read(byte b[]) 方法调用了 read(byte b[], int off, int len) 方法,所以在此处重点看的方法是带三个参数的方法。

在该方法中第18行、27行,可以看到调用了无参的抽象的 read() 方法。

总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现。

后记

喜欢我的文章的朋友点点喜欢、收藏,也欢迎朋友们评论区留下你的意见和建议,恕毅在此拜谢!

相关文章
|
21天前
|
敏捷开发 开发框架 测试技术
从零开始:如何快速开发一个软件?实用方法 + 避坑指南
本文介绍了如何在保证质量的前提下快速开发软件,涵盖从需求分析到上线推广的完整流程。内容包括敏捷开发、MVP设计、工具选择、团队协作与持续运营等关键环节,适合初创团队、企业试点及个人开发者参考,帮助高效验证产品价值,缩短开发周期,降低试错成本。
|
设计模式 弹性计算 人工智能
阿里技术专家详解DDD系列 第四讲 - 领域层设计规范
在一个DDD架构设计中,领域层的设计合理性会直接影响整个架构的代码结构以及应用层、基础设施层的设计。但是领域层设计又是有挑战的任务,特别是在一个业务逻辑相对复杂应用中,每一个业务规则是应该放在Entity、ValueObject 还是 DomainService是值得用心思考的,既要避免未来的扩展性差,又要确保不会过度设计导致复杂性。
|
安全 关系型数据库 MySQL
Linux云服务器CentOS7版本下安装mysql5.7
Linux云服务器CentOS7版本下安装mysql5.7
|
Oracle 关系型数据库 MySQL
OceanBase有什么特性?
OceanBase有什么特性?【8月更文挑战第12天】
342 0
|
Web App开发 监控 JavaScript
【Node系列】REPL详解
Node.js REPL(Read-Eval-Print Loop)是一个交互式环境,允许用户在命令行中直接输入JavaScript代码并立即看到结果。REPL是Node.js的一个重要组成部分,它提供了一个方便的方式来测试代码片段、快速尝试新功能或进行调试。
273 1
|
机器学习/深度学习 自然语言处理 前端开发
2024年3月最新的深度学习论文推荐
现在已经是3月中旬了,我们这次推荐一些2月和3月发布的论文。
776 0
|
数据采集 算法 前端开发
【MATLAB】 稳健的经验模式分解REMD信号分解算法
【MATLAB】 稳健的经验模式分解REMD信号分解算法
384 0
|
存储 Java
SpringBoot中过滤器如何设置执行顺序
SpringBoot中过滤器如何设置执行顺序
1158 0
|
JavaScript 前端开发 安全
java动态脚本执行效率对比评测
本文作者针对实际场景及需求特性,经过安全性,易用性等综合评估,再结合工作场景选择javascript、lua、原生java进行性能测评。
870 0
java动态脚本执行效率对比评测
|
存储 JavaScript 前端开发
区块链代币支付钱包(trx/trc20代币/usdt)对接开发
区块链代币支付钱包(trx/trc20代币/usdt)对接开发

热门文章

最新文章