第十三章行为型模式—模板模式

news/2024/11/13 4:09:29/

文章目录

  • 模板模式
    • 解决的问题
    • 结构
    • 实例
    • 存在的问题
    • 适用场景
  • JDK源码 - InputStream

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

  • 类行为模式:采用继承机制来在类间分派行为

  • 对象行为模式:采用组合或聚合在对象间分配行为

由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式分为:

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

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

模板模式

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

  • 其核心是将一些行为延迟到子类中进行实现

解决的问题

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

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

结构

抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

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

  • 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:

    • 抽象方法 (Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

      • 具体方法 (Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

      • 钩子方法 (Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

        • 一般钩子方法是用于判断的逻辑方法,这类方法名一般为 isXxx,返回值类型为 boolean 类型。

具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

实例

炒菜

炒菜的步骤是固定的,分为【倒油】、【热油】、【倒蔬菜】、【倒调料品】、【翻炒】等步骤。

现通过模板方法模式来用代码模拟,类图如下:

image-20230516162850482

抽象类:定义模板方法和抽象方法。

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

public abstract class AbstractClass {// 模板方法定义public final void cookProcess() {pourOil();heatOil();pourVegetable();pourSauce();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{@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是辣椒");}
}
public class ConcreteClass_CaiXin  extends AbstractClass{@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是蒜蓉");}
}

测试类

public class Client {public static void main(String[] args) {// 炒包菜// 创建对象ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();// 调用炒菜的功能baoCai.cookProcess();}
}
//倒油
//热油
//下锅的蔬菜是包菜
//下锅的酱料是辣椒
//炒啊炒啊炒到熟啊

存在的问题

优点:

  • 提高代码复用性:将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。

  • 实现了反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,符合“开闭原则”。

缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

适用场景

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

JDK源码 - InputStream

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(); // 调用了无参的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) 方法,所以在此处重点看的方法是带三个参数的方法。

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

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


http://www.ppmy.cn/news/119293.html

相关文章

SkyLine简介

简介 SkyLine产品系列&#xff08;TerraExplorer 、TerraGate、TerraBuilder&#xff09;是一套优秀的三维数字地球平台软件。凭借其国际领先的三维数字化显示技术&#xff0c;它可以利用海量的遥感航测影像数据、数字高程数据以及其他二三维数据搭建出一个对真实世界进行模拟…

计算如何与实验结合发Science

理论计算与实验结合的研究已经成为TOP期刊中的主流方式。近日&#xff0c;上海交通大学种丽娜副教授一项关于质子交换膜水解槽阳极催化剂的研究成果在Science发表。该工作报道了一种由沸石甲基咪唑酯骨架&#xff08;Co-ZIF&#xff09;衍生并通过静电纺丝处理的镧和锰共掺杂的…

第十六章行为性模式—职责链模式

文章目录 职责链模式解决的问题结构实例存在的问题 JavaWeb 源码 - FilterChain 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。行为型模式分为类…

函数栈帧的创建与销毁

目录 1、寄存器的分类与作用 2、测试代码与所需要的知识点 2.1、测试代码 2.2 、关于main函数的调用 2.3、关于栈、压栈、出栈简介 3、main函数栈帧的创建与分析 3 .1、push edp 3.2、mov ebp&#xff0c;esp 3.3、sub esp&#xff0c;0E4h 3.4、push ebx&#xf…

40V 高精度 LDO 稳压器-QX6126

QX6126 是一款支持最高 40V 输入的 三端 LDO 稳压器芯片。其内置高精度的 输出运算放大器&#xff0c;进而可得到精准且稳定 的输出电压。 芯片具有低静态电流&#xff0c;并可实现最大 100mA 的电流输出&#xff0c;并具有短路保护、温 度保护等功能。 QX6126 采用 SOT23、S…

STM32自学笔记-5-SPI和Flash芯片2

在W25Qxx.c中&#xff0c;可以重点看以下几个函数 BSP_W25Qx_WriteEnable()函数 uint8_t BSP_W25Qx_WriteEnable(void) {uint8_t cmd[] {0x06};uint32_t tickstart HAL_GetTick(); //开始计时W25Qx_Enable(); //将NSS拉低&#xff0c;使芯片可操作HAL_SPI_Transmit(&h…

LED台灯方案:QX6126+QX5567

QX6126是一款支持40V输入的三端LDO稳压器芯片。其内置高精度的输出运算放大器&#xff0c;进而可得到精准且稳定的输出电压。 芯片具有低静态电流&#xff0c;并可实现最大100mA的电流输出&#xff0c;并具有短路保护、温度保护等功能。 QX6126采用SOT-23-3、SOT-89封装&…

小风扇专用芯片-QX5311F

概述 QX5311F是一款集成了锂电池线性充电箮和三种档位输出驱动的手持风扇驱动芯片&#xff0c;只需极少的外接元件&#xff0c;便能适用于手持风扇等便携式产品的应用。 QX5311F 根据电池电压的不同可分别有涓流充电&#xff0c;恒流充电和恒压充电等三种充电模式。浮流电压被固…