软件设计模式—模板方法

模板方法(行为模式)

一、概念解释

模板方法模式就像是制定了一份做事的 “通用模板” 或者 “标准流程”。这个模板里规定好了做事的主要步骤,但是其中某些步骤的具体做法可以根据不同的情况灵活变化。

打个比方,你要制作一份美食,美食的制作有固定的几个大步骤,比如准备食材、烹饪、装盘,但不同的美食在准备食材、烹饪方式等具体操作上会有差异,这个固定的大步骤就是模板,而每个步骤的具体做法就是可以灵活调整的部分。

二、生活场景示例

以制作饮料为例,假设你要制作不同类型的热饮,像咖啡和茶,它们的制作过程有一些通用步骤。

1.通用流程:

      1. 烧开水。
      2. 用开水冲泡相应的原料(咖啡粉或茶叶)。
      3. 把泡好的饮品倒入杯子。
      4. 可以根据个人口味添加调料(糖、牛奶等)。

2.具体差异:

在冲泡原料这一步,制作咖啡用的是咖啡粉,而制作茶用的是茶叶;添加调料时,有些人喝咖啡喜欢加牛奶和糖,而有些人喝茶可能什么都不加。

三、主要元素

元素 功能 代码实现
通用模板方法或流程载体 1. 定义了模板方法 2. 作为子步骤的一些抽象方法或具体方法。 3. 钩子方法(可选),用来决定流程中的某步骤是否执行。 抽象父类 注意:在设计时候,确保具体子类不能修改流程部分代码。方法使用关键字final(java)或sealed(C#)
具体模板或流程载体 继承自抽象父类,并实现子步骤的抽象方法 具体子类
使用者 使用抽象父类和具体子类提供的功能,实现其他想要的功能

四、代码示例及解释

1.下面用 Java 代码来实现上述制作饮料的场景。


// 抽象类,定义制作饮料的模板方法

abstract class BeverageMaker {

// 模板方法,定义制作饮料的通用流程

public final void makeBeverage() {

boilWater();

brew();

pourInCup();

if (customerWantsCondiments()) {

addCondiments();

}

}

// 烧开水,具体实现固定

private void boilWater() {

System.out.println("Boiling water");

}

// 冲泡原料,具体实现由子类决定

protected abstract void brew();

// 倒入杯子,具体实现固定

private void pourInCup() {

System.out.println("Pouring into cup");

}

// 添加调料,具体实现由子类决定

protected abstract void addCondiments();

// 钩子方法,判断顾客是否需要调料,默认需要

protected boolean customerWantsCondiments() {

return true;

}

}

// 具体子类,咖啡制作类

class CoffeeMaker extends BeverageMaker {

@Override

protected void brew() {

System.out.println("Dripping coffee through filter");

}

@Override

protected void addCondiments() {

System.out.println("Adding sugar and milk");

}

// 重写钩子方法,根据实际情况决定是否添加调料

@Override

protected boolean customerWantsCondiments() {

return false;

}

}

// 具体子类,茶制作类

class TeaMaker extends BeverageMaker {

@Override

protected void brew() {

System.out.println("Steeping the tea");

}

@Override

protected void addCondiments() {

System.out.println("Adding lemon");

}

}

// 使用者,测试类

public class BeverageTest {

public static void main(String[] args) {

System.out.println("Making coffee...");

BeverageMaker coffeeMaker = new CoffeeMaker();

coffeeMaker.makeBeverage();

System.out.println("\nMaking tea...");

BeverageMaker teaMaker = new TeaMaker();

teaMaker.makeBeverage();

}

}

2.C#实现代码:


using System;

// 抽象类,定义制作饮料的模板方法

abstract class BeverageMaker

{

// 模板方法,定义制作饮料的通用流程

public sealed void MakeBeverage()

{

BoilWater();

Brew();

PourInCup();

if (CustomerWantsCondiments())

{

AddCondiments();

}

}

// 烧开水,具体实现固定

private void BoilWater()

{

Console.WriteLine("Boiling water");

}

// 冲泡原料,具体实现由子类决定

protected abstract void Brew();

// 倒入杯子,具体实现固定

private void PourInCup()

{

Console.WriteLine("Pouring into cup");

}

// 添加调料,具体实现由子类决定

protected abstract void AddCondiments();

// 钩子方法,判断顾客是否需要调料,默认需要

protected virtual bool CustomerWantsCondiments()

{

return true;

}

}

// 具体子类,咖啡制作类

class CoffeeMaker : BeverageMaker

{

protected override void Brew()

{

Console.WriteLine("Dripping coffee through filter");

}

protected override void AddCondiments()

{

Console.WriteLine("Adding sugar and milk");

}

// 重写钩子方法,根据实际情况决定是否添加调料

protected override bool CustomerWantsCondiments()

{

return false;

}

}

// 具体子类,茶制作类

class TeaMaker : BeverageMaker

{

protected override void Brew()

{

Console.WriteLine("Steeping the tea");

}

protected override void AddCondiments()

{

Console.WriteLine("Adding lemon");

}

}

//使用者

class Program

{

static void Main()

{

Console.WriteLine("Making coffee...");

BeverageMaker coffeeMaker = new CoffeeMaker();

coffeeMaker.MakeBeverage();

Console.WriteLine("\nMaking tea...");

BeverageMaker teaMaker = new TeaMaker();

teaMaker.MakeBeverage();

}

}

代码解释:

通过模板方法模式,我们可以在保证整体流程不变的情况下,灵活地改变某些步骤的具体实现。

  1. BeverageMaker 抽象类
    • MakeBeverage 方法是模板方法,它定义了制作饮料的通用步骤,包含了烧开水、冲泡原料、倒入杯子以及根据条件添加调料等步骤。
    • BoilWater 和 PourInCup 方法的实现是固定的,因为烧开水和倒入杯子的操作对于制作咖啡和茶来说是一样的。因而使用 private 访问修饰符,表明这是内部实现细节,不希望子类直接访问或重写。
    • Brew 和 AddCondiments 方法,在不同饮料制作中存在差异。因而被声明为 protected abstract,这意味着它们是抽象方法,具体的实现由子类完成。
    • CustomerWantsCondiments 是一个钩子方法,子类可以根据需要重写它,从而决定是否要执行添加调料这一步骤。使用 protected virtual 修饰,允许子类重写该方法来改变是否添加调料的决策逻辑,默认返回 true。
  2. CoffeeMaker 类和 TeaMaker 类
    • 这两个类继承自 BeverageMaker 抽象类,并实现了抽象方法 Brew 和 AddCondiments,以体现制作咖啡和茶在冲泡原料和添加调料上的不同。
    • CoffeeMaker 类重写了 CustomerWantsCondiments 方法,返回 false,表示制作咖啡时不添加调料。
  3. Program 类的 Main 方法
    • 创建了 CoffeeMaker 和 TeaMaker 的实例,并调用它们的 MakeBeverage 方法来执行制作饮料的流程。

五、使用场景

当存在一些通用的算法或者流程骨架结构不变,而其步骤中某些具体操作不同的时候,可以使用。常见如下场景:

1. 框架开发

框架开发者明确算法整体结构与通用步骤,但具体实现因场景而异。如 Java 的 Servlet 框架,HttpServlet 类定义了处理 HTTP 请求的通用流程,doGet、doPost 等方法供开发者重写实现业务逻辑。

2. 多子类共性场景

多个子类有共同行为和核心算法,但部分步骤实现不同。例如需要将多种类型的文档(如文本文件、电子表格、演示文稿)转换为 PDF 格式。整个转换过程包含从先读取、再解析、后生成的通用步骤,而每种文件的具体读取、解析和生成的处理细节却不同,可在父类定义通用流程,子类实现每一步的细节。

3. 代码重构优化

代码存在大量重复且代表通用算法流程时,可用此模式重构。像数据处理系统,各模块都有数据读取、处理、存储步骤,但方式不同,可将通用流程放父类模板方法,具体实现由子类完成。

4. 控制子类扩展

通过模板方法和钩子方法控制子类对算法流程的扩展。模板方法规定步骤顺序,钩子方法允许子类特定点扩展或修改。如工作流审批系统,父类定义通用审批流程,子类实现各步骤,钩子方法可控制步骤跳过或额外操作。

posted @   码客风云  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
历史上的今天:
2020-02-14 如何用代码来快速批量下载人教社中小学电子教材

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示