模板方法模式详解

embedded/2025/1/16 2:31:26/

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在方法中定义算法的框架,延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。下面对模板方法模式进行详细讲解:
一、角色
1.  抽象类(Abstract Class)
•  它定义了一个或多个抽象方法,这些抽象方法由子类实现。抽象类中还定义了一个模板方法,模板方法是一个具体方法,它给出了一个算法的轮廓和骨架,并调用抽象方法。
•  例如,在一个文档处理系统中,抽象类可以是DocumentProcessor,它定义了一个抽象方法processContent()用于处理文档内容,还定义了一个模板方法processDocument(),该方法的步骤包括打开文档、处理文档内容、保存文档等,其中处理文档内容的步骤调用了processContent()抽象方法。
2.  具体子类(Concrete Subclass)
•  它实现父类的抽象方法,但不改变模板方法所定义的算法结构。具体子类根据实际的业务需求,实现抽象方法中的具体逻辑。
•  继续上面的例子,具体子类可以是TextDocumentProcessor和PDFDocumentProcessor。TextDocumentProcessor实现了processContent()方法,具体逻辑是解析文本格式的内容;PDFDocumentProcessor也实现了processContent()方法,其逻辑是解析PDF格式的内容。这两个子类都没有改变processDocument()模板方法所定义的打开、处理、保存的算法结构。
二、优点
1.  封装不变部分,扩展可变部分
•  模板方法模式将不变的算法步骤封装在父类的模板方法中,而将可变的步骤通过抽象方法延迟到子类实现。这样,子类只需要关注实现可变部分的逻辑,而不需要关心整个算法的流程。例如,在一个电商订单处理系统中,订单处理的流程(如验证订单、处理支付、发货、完成订单等步骤)是相对固定的,但不同类型的订单(如普通订单、预售订单、海外订单等)在处理支付和发货等具体步骤上可能有所不同。使用模板方法模式,可以将订单处理的固定流程封装在抽象类的模板方法中,而将不同类型的订单处理支付和发货等可变步骤通过抽象方法让子类去实现,从而提高了代码的复用性和可维护性。
2.  行为由父类控制,子类实现
•  父类通过模板方法定义了算法的执行顺序和流程控制,子类通过实现抽象方法来提供具体的行为实现。这种模式使得算法的控制逻辑集中在父类中,子类只需要按照父类定义的框架来实现具体的行为,降低了子类之间的耦合度。比如在一个游戏的角色技能释放系统中,技能释放的流程(如技能准备、技能执行、技能结束等)由父类的模板方法控制,而不同角色的具体技能效果(如火球术、冰冻术等)由子类实现,这样可以方便地管理和扩展角色的技能系统。
3.  提取公共代码,便于维护
•  通过将算法的公共部分提取到父类的模板方法中,可以避免代码重复。当需要修改公共部分的逻辑时,只需要在父类中修改模板方法,而不需要在每个子类中都进行修改,从而提高了代码的可维护性。例如,在一个图形绘制系统中,绘制图形的流程(如设置画笔、绘制图形、释放画笔等)有公共部分,使用模板方法模式将这些公共步骤放在父类的模板方法中,当需要优化画笔设置的逻辑时,只需修改父类中的相关代码即可。
三、缺点
1.  违背开闭原则
•  如果算法的流程需要改变,那么就需要修改模板方法,这可能会违反开闭原则。因为开闭原则要求软件实体应该对扩展开放,对修改关闭,而修改模板方法意味着要修改父类的代码。例如,在上面的文档处理系统例子中,如果需要在文档处理流程中增加一个校验文档格式的步骤,可能就需要修改DocumentProcessor抽象类中的processDocument()模板方法,这就需要修改父类代码。
2.  限制子类的灵活性
•  模板方法定义了算法的固定流程,子类只能在父类定义的框架内进行扩展,这在一定程度上限制了子类的灵活性。如果子类需要进行一些特殊的操作,但父类的模板方法没有提供相应的扩展点,子类可能就无法实现这些特殊操作。比如在订单处理系统中,如果父类的模板方法没有提供在发货前进行特殊包装的扩展点,那么对于一些需要特殊包装的订单(如易碎品订单),子类可能就无法很好地实现这一特殊需求。
四、应用场景
1.  算法步骤固定,细节可变场景
•  当一个算法的步骤是固定的,但每个步骤的具体实现细节可能会根据不同的情况而变化时,适合使用模板方法模式。例如,在一个在线教育平台的课程学习流程中,学习流程(如观看视频、完成测验、提交作业、获取证书等步骤)是固定的,但不同类型的课程(如语言类课程、技术类课程等)在完成测验和提交作业等具体步骤的实现上可能会有所不同,可以使用模板方法模式来实现。
2.  多子类共用方法,避免代码重复场景
•  当多个子类有共用的方法,并且逻辑相同,但子类的某些细节需要不同的实现时,可以使用模板方法模式来提取共用的方法到父类中,避免代码重复。比如在一个物流配送系统中,不同类型的货物(如普通货物、生鲜货物、危险品货物等)的配送流程(如接单、配送、签收等步骤)有共用的部分,但配送过程中的具体操作(如生鲜货物需要冷藏配送等)有所不同,可以使用模板方法模式来实现。
3.  控制子类扩展场景
•  当需要控制子类的扩展,确保子类不改变算法的整体结构时,模板方法模式是一个很好的选择。例如,在一个软件的权限管理系统中,用户登录的流程(如输入用户名和密码、验证身份、授权等步骤)需要严格控制,不允许子类随意改变,但不同类型的用户(如普通用户、管理员用户等)在授权的具体细节上可能有所不同,可以使用模板方法模式来实现。
五、实现示例(以Java语言为例)
假设我们要实现一个简单的烤咖啡的过程,使用模板方法模式来实现如下:
// 抽象类:咖啡制作抽象类
public abstract class CoffeeMaker {
    // 模板方法,定义了烤咖啡的流程
    public final void makeCoffee() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    // 抽象方法,由子类实现具体的冲泡方式
    protected abstract void brew();

    // 抽象方法,由子类实现具体的添加配料方式
    protected abstract void addCondiments();

    // 具体方法,加热水
    private void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体方法,将咖啡倒入杯子
    private void pourInCup() {
        System.out.println("Pouring into cup");
    }
}

// 具体子类:制作浓咖啡
public class StrongCoffeeMaker extends CoffeeMaker {
    @Override
    protected void brew() {
        System.out.println("Dripping Strong Coffee through filter");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 具体子类:制作卡布奇诺
public class CappuccinoMaker extends CoffeeMaker {
    @Override
    protected void brew() {
        System.out.println("Steaming milk");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding steamed milk foam");
    }
}

// 客户端代码
public class TemplateMethodDemo {
    public static void main(String[] args) {
        CoffeeMaker strongCoffeeMaker = new StrongCoffeeMaker();
        strongCoffeeMaker.makeCoffee();

        CoffeeMaker cappuccinoMaker = new CappuccinoMaker();
        cappuccinoMaker.makeCoffee();
    }
}

在这个例子中,CoffeeMaker抽象类定义了一个模板方法makeCoffee(),它给出了烤咖啡的流程,包括加热水、冲泡、将咖啡倒入杯子、添加配料等步骤。其中,冲泡和添加配料的步骤是抽象方法,由子类实现具体的逻辑。StrongCoffeeMaker和CappuccinoMaker是具体子类,分别实现了冲泡浓咖啡和制作卡布奇诺的具体逻辑。客户端代码通过创建不同的子类对象来制作不同类型的咖啡,而整个烤咖啡的流程是由父类的模板方法控制的。


http://www.ppmy.cn/embedded/154279.html

相关文章

Git | git reset命令详解

关注:CodingTechWork 引言 Git 是一款非常流行的分布式版本控制工具,它帮助开发者有效地管理代码历史,支持多种功能来帮助团队协作、追踪修改和维护代码质量。git reset是 Git 中最强大、最复杂的命令之一,它的主要作用是重置当前…

【01】AE特效开发制作特技-Adobe After Effects-AE特效制作快速入门-制作飞机,子弹,爆炸特效以及导出png序列图-优雅草央千澈

【01】AE特效开发制作特技-Adobe After Effects-AE特效制作快速入门-制作飞机,子弹,爆炸特效以及导出png序列图-优雅草央千澈 开发背景 优雅草央千澈所有的合集,系列文章可能是不太适合完全初学者的,因为课程不会非常细致的系统…

flutter VoidCallBack ValueChange<T> 的函数定义

在 Flutter 中&#xff0c;VoidCallback 和 ValueChanged<T> 是两种常用的回调函数类型&#xff0c;它们通常用于处理事件或传递数据。下面是它们的详细定义及使用方式。 1. VoidCallback 函数类型 VoidCallback 是一个没有参数也没有返回值的回调函数类型。它通常用于…

windows wsl ubuntu22 远程桌面连接

转载链接&#xff1a;https://canwdev.github.io/VM%E8%99%9A%E6%8B%9F%E6%9C%BA/WSL/wsl2%20wslg%20%E9%85%8D%E7%BD%AE%E5%B9%B6%E5%BC%80%E5%90%AF%E8%BF%9C%E7%A8%8B%E6%A1%8C%E9%9D%A2%28xrdp%29/ Wsl2 wslg 配置并开启远程桌面(xrdp) 准备工作 推荐到微软应用商店下载最…

61_Redis服务器端优化

1.持久化配置 Redis的持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化请遵循下列建议: 用来做缓存的Redis实例尽量不要开启持久化功能建议关闭RDB持久化功能,使用AOF持久化利用脚本定期在slave节点做RDB,实现数据备份设置合理的rewrite阈值,避免频繁的…

Ubuntu下安装Harbor

安装Harbor 1.Harbor简介1.1 关键特性1.2 使用场景2.安装2.0 前置要求2.0.1 硬件2.0.2 软件2.0.3 网络2.1 下载2.2 配置2.3 安装2.4 登录web-UI2.5 创建一个项目2.6 推送2.7 拉取3 服务启停在ubuntu24.04.1下安装harbor 1.Harbor简介 Harbor 是一个开源的 Docker registry 服务…

《深入理解Mybatis原理》Mybatis中的缓存实现原理

一级缓存实现 什么是一级缓存&#xff1f; 为什么使用一级缓存&#xff1f; 每当我们使用MyBatis开启一次和数据库的会话&#xff0c;MyBatis会创建出一个SqlSession对象表示一次数据库会话。 在对数据库的一次会话中&#xff0c;我们有可能会反复地执行完全相同的查询语句&…

【2024年华为OD机试】(C卷,100分)- 最大股票收益 (Java JS PythonC/C++)

一、问题描述 题目描述 假设知道某段连续时间内股票价格&#xff0c;计算通过买入卖出可获得的最大收益。 输入一个大小为 n 的数 price(p1,p2,p3,p4…….pn),pi 是第i天的股票价格。 pi 的格式为股票价格&#xff08;非负整型&#xff09;加上货币单位 Y 或者 S,其中 Y 代…