当前位置: 首页 > article >正文

模板方法模式详解

模板方法模式(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.kler.cn/a/502987.html

相关文章:

  • Java 泛型的用法
  • 【STM32-学习笔记-6-】DMA
  • ASP.NET Core - 日志记录系统(二)
  • 零基础 监控数据可视化 Spring Boot 2.x(Actuator + Prometheus + Grafana手把手) (上)
  • Spring Boot 应用开发入门
  • 【fly-iot飞凡物联】(19):开源飞凡物联项目重启,使用go重写后端代码,感兴趣的小伙伴可以一起参加,使用apache协议开源,招募感兴趣的小伙伴!!
  • 虚拟拨号技术(GOIP|VOIP)【基于IP的语音传输转换给不法分子的境外来电披上一层外衣】: Voice over Internet Protocol
  • git——merge和rebase
  • 探索网络安全:浅析文件上传漏洞
  • Mysql--运维篇--备份和恢复(逻辑备份,mysqldump,物理备份,热备份,温备份,冷备份,二进制文件备份和恢复等)
  • python+pymysql
  • HarmonyOS:@LocalBuilder装饰器: 维持组件父子关系
  • Android Dex VMP 动态加载加密指令流
  • ucharts写的小程序如何解决Y轴小数点问题
  • 《机器学习》之K-means聚类
  • Docker数据管理与网络
  • 科研绘图系列:R语言绘制Y轴截断分组柱状图(y-axis break bar plot)
  • Tox,Python 测试环境的自动化管理器,一个高效操作的 Python 工具!
  • RabbitMQ-延迟交换器
  • 【C】初阶数据结构2 -- 顺序表