【行为型模式】模板方法模式

news2024/10/6 8:38:08

文章目录

    • 优秀借鉴
    • 1、概述
    • 2、结构
    • 3、实现方式
      • 3.1、案例引入
      • 3.2、结构分析
      • 3.3、具体实现
    • 4、模板方法模式优缺点
    • 5、应用场景

优秀借鉴

  • 黑马程序员Java设计模式详解-模板方法模式概述

1、概述

模板方法模式(Template Method)是一种设计模式,它定义了一个算法的框架,并将具体步骤延迟到子类中实现。在该模式中,父类中定义一个模板方法来描述算法的基本流程,在这个过程中,某些步骤可以通过抽象方法或空置来延迟到子类中实现。

通常情况下,当我们处理一些相似的任务时,会发现这些任务之间有很多共性,只是其中一些步骤不同而已。如果每次都重复编写代码,既费时又容易出错。因此,为了解决这个问题,就产生了模板方法模式,它把这些共性代码抽象到父类中,子类只需要覆盖特定的步骤即可。

2、结构

模板方法模式包含以下几个角色:

  1. 抽象类(Abstract Class):定义抽象方法和模板方法,抽象方法由子类实现,模板方法描述算法的基本流程,并使用抽象方法来延迟到子类中实现:

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

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

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

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

    • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

  2. 具体类(Concrete Class):继承自抽象类,实现抽象方法。

uml

3、实现方式

3.1、案例引入

可以将模板方法模式类比于制作蛋糕的过程。在制作蛋糕时,有很多共性的步骤,例如准备材料、打好鸡蛋液、混合面粉和糖等,但是每种蛋糕可能会有一些特殊的步骤或要求。如果我们针对每种蛋糕都写一份完整的制作流程,那么代码量会非常大,而且维护起来也不方便。

因此,我们可以使用模板方法模式来解决这个问题。我们可以定义一个抽象的蛋糕制作类,其中包含了一系列基本的制作步骤,例如准备材料、打好鸡蛋液、混合面粉和糖等。然后,我们再定义具体的蛋糕制作类,例如水果蛋糕制作类、巧克力蛋糕制作类等,它们继承自抽象的蛋糕制作类,并实现其中的抽象方法,例如加入水果、加入巧克力等。

当需要制作某种特定的蛋糕时,我们只需要选择相应的具体类即可,无需重复编写制作流程,代码更加简洁易懂,也更易于维护。

案例uml

3.2、结构分析

在上述案例中,模板方法模式的角色分别对应如下:

  1. 抽象类 Cake:是一个抽象的蛋糕制作类,其中包含了一系列基本的制作步骤,并定义了一个模板方法 make() 来封装这些基本步骤:

    • make() 方法:是一个具体方法,它包含了一系列基本的制作步骤,并且定义了整个算法的流程和骨架。它通过调用抽象方法和钩子方法来完成算法中的特殊步骤和可选步骤;
    • addSpecialIngredient() 方法:是一个抽象方法,用于在蛋糕制作中添加特殊食材。由于不同的蛋糕需要添加不同的特殊食材,因此将其定义为抽象方法,由子类实现;
    • needFrosting() 方法:是一个钩子方法,用于判断是否需要覆盖蛋糕。由于不同的蛋糕可能需要或者不需要覆盖,因此将其定义为钩子方法,由子类决定是否进行覆盖;
    • prepareIngredients()mixEggLiquid()mixFlourAndSugar()bake()addFrosting() 方法:都是具体方法,用于实现蛋糕制作中的基本步骤。这些方法在模板方法中被调用,但是它们的具体实现可以被子类所重写;
  2. 具体类 FruitCakeChocolateCake:是 Cake 的具体子类,它们各自实现了抽象方法 addSpecialIngredient()needFrosting() 来完成特殊的步骤,并对需要覆盖的基本步骤进行了重写。

3.3、具体实现

好的,这里是使用中文描述并且添加了方法注释的 Java 代码:

首先,我们定义一个抽象的蛋糕制作类 Cake,其中包含了一系列基本的制作步骤:

public abstract class Cake {

    /**
     * 制作蛋糕的模板方法
     */
    public final void make() {
        // 准备材料
        prepareIngredients(); 
        // 打好鸡蛋液
        mixEggLiquid(); 
        // 混合面粉和糖
        mixFlourAndSugar(); 
        // 加入特殊食材
        addSpecialIngredient(); 
        // 在烤箱中烘烤
        bake(); 

        if (needFrosting()) {
            // 如果需要覆盖,再添加霜糖
            addFrosting(); 
        }
    }

    /**
     * 抽象方法:加入特殊食材
     */
    protected abstract void addSpecialIngredient();

    /**
     * 抽象方法:是否需要覆盖
     */
    protected abstract boolean needFrosting();

    /**
     * 准备材料
     */
    private void prepareIngredients() {
        System.out.println("准备材料...");
    }

    /**
     * 打好鸡蛋液
     */
    private void mixEggLiquid() {
        System.out.println("打好鸡蛋液...");
    }

    /**
     * 混合面粉和糖
     */
    private void mixFlourAndSugar() {
        System.out.println("混合面粉和糖...");
    }

    /**
     * 在烤箱中烘烤
     */
    private void bake() {
        System.out.println("在烤箱中烘烤...");
    }

    /**
     * 添加霜糖
     */
    private void addFrosting() {
        System.out.println("给蛋糕上添加霜糖...");
    }
}

在其中,make() 方法就是模板方法,其中包含了一系列基本的制作步骤。其中涉及到的一些特殊步骤由抽象方法 addSpecialIngredient()needFrosting() 来延迟到子类中实现。

然后,我们定义一个具体的水果蛋糕制作类 FruitCake,它继承自抽象的蛋糕制作类,并实现其中的抽象方法:

public class FruitCake extends Cake {

    /**
     * 加入特殊食材:水果
     */
    @Override
    protected void addSpecialIngredient() {
        System.out.println("将水果加入蛋糕中...");
    }

    /**
     * 是否需要覆盖:不需要
     */
    @Override
    protected boolean needFrosting() {
        return false;
    }
}

在其中,addSpecialIngredient() 方法实现了加入水果这一特殊步骤,needFrosting() 方法则表明这种蛋糕不需要覆盖。

最后,我们定义一个具体的巧克力蛋糕制作类 ChocolateCake,它同样继承自抽象的蛋糕制作类,并实现其中的抽象方法:

public class ChocolateCake extends Cake {

    /**
     * 加入特殊食材:巧克力
     */
    @Override
    protected void addSpecialIngredient() {
        System.out.println("将巧克力加入蛋糕中...");
    }

    /**
     * 是否需要覆盖:需要
     */
    @Override
    protected boolean needFrosting() {
        return true;
    }
}

在其中,addSpecialIngredient() 方法实现了加入巧克力这一特殊步骤,needFrosting() 方法则表明这种蛋糕需要覆盖。

最后,我们可以通过以下代码来测试上述代码:

public class Test {
    public static void main(String[] args) {
        Cake fruitCake = new FruitCake();
        fruitCake.make();

        System.out.println("-----------------------");

        Cake chocolateCake = new ChocolateCake();
        chocolateCake.make();
    }
}

运行结果如下:

准备材料...
打好鸡蛋液...
混合面粉和糖...
将水果加入蛋糕中...
在烤箱中烘烤...
-----------------------
准备材料...
打好鸡蛋液...
混合面粉和糖...
将巧克力加入蛋糕中...
在烤箱中烘烤...
给蛋糕上添加霜糖...

4、模板方法模式优缺点

模板方法模式的优点:

  1. 提高代码复用性:将相同的部分抽象出来成为模板,避免重复的代码,同时提高了代码的复用性。

  2. 便于维护:模板方法模式提供了稳定的结构,使得子类的实现变得简单明了,也更容易进行维护。

  3. 实现了反向控制:通过把不变行为的控制权交给父类,将变化行为留给子类来实现,实现了反向控制,符合“开闭原则”。

模板方法模式的缺点:

  1. 模板方法模式会带来一定的复杂度,增加了代码结构和抽象层次。

  2. 在具体实现繁琐多变的情况下,并不适合使用模板方法模式。

优点缺点
提高代码复用性带来一定的复杂度
便于维护在具体实现繁琐多变的情况下,并不适合使用模板方法模式
实现了反向控制,符合“开闭原则”

5、应用场景

模板方法模式通常用于以下场景:

  1. 父类和子类之间共享一组行为或算法,并且其实现步骤基本相同,只有个别步骤的实现有所不同。

  2. 需要在多个项目或多个版本中重复使用相同的代码逻辑。

  3. 需要在代码运行时动态地指定某些部分的行为,而不是静态地实现在代码中。

  4. 多个程序或系统中存在类似的业务流程,但其中个别步骤的实现有所不同,可以使用模板方法模式将这些不同的实现抽象出来,由具体的子类去实现。

模板方法模式适用于一些希望通过把固定流程的代码封装到基类中的方式来控制子类行为的情况。它可用于框架设计、通用算法设计等场景。

以下是一些Java和Spring中的模板方法模式的例子:

  1. java.util.AbstractList:在Java集合框架中,AbstractList 是一个具体的抽象类,它实现了 List 接口,并提供了大量公共的方法。AbstractList 的子类需要实现的是 get()size() 方法。

  2. java.io.InputStream:用于输入字节流的抽象类,它提供了很多与读取字节流相关的方法,如 read(), skip(), available() 等等。

  3. javax.servlet.http.HttpServlet:是 Servlet 的抽象类,定义了 service() 方法作为 HTTP 请求抽象处理的入口点。开发者继承此类并重写 service() 方法即可实现自己的 Servlet。

  4. org.springframework.jdbc.core.JdbcTemplate:Spring JDBC 模块提供了 JdbcTemplate 类来简化数据库访问。JdbcTemplate 是一个具体的类,由 Spring 框架实现的,它使用模板方法设计模式来执行 SQL 查询、更新、存储过程调用等操作。开发者只需要继承 JdbcTemplate 并实现其抽象方法即可实现自己的数据访问操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/416382.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos 2.2.2 发布,优化启动体验和鉴权提示

Nacos 社区近期发布了 2.2.0.1 和 2.2.1 版本,对默认鉴权插件做出了较大的改动,详情查看**风险说明 [ 1] **及 **2.2.1 发布 [ 2] **。由于 Nacos 默认控制台 ui 中默认依赖了 token.secret.key,所以在移除了 token.secret.key 的默认值后&am…

【ROS2指南-9】Bag的record和play操作

目标:记录在某个话题上发布的数据,以便您可以随时回放和检查它。 教程级别:初学者 时间: 10分钟 内容 背景 先决条件 任务 1 设置 2 选择一个主题 3 ros2包记录 4 ros2 包信息 5 ros2包玩 概括 下一步 相关内容 背景 r…

Android 性能优化的重要性~

随着移动设备和应用程序市场不断发展,Android应用程序变得越来越多,对于开发者来说,他们必须使自己的应用程序与其他应用程序相比更加高效和快速,以吸引用户和确保业务成功。而Android用户期望应用程序如同其他设备上的应用程序一…

PHP快速入门02-PHP语言基础

文章目录前言一、 数据类型1.1 String(字符串)1.2 Integer(整型)1.3 Float(浮点型)1.4 Boolean(布尔型)1.5 Array(数组)1.6 Object(对象&#xff…

[ vulnhub靶机通关篇 ] 渗透测试综合靶场 DC-5 通关详解 (附靶机搭建教程)

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

深入了解Hugging Face Transformers库:NLP的转换之力

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

29岁,普通功能测试,我是如何在一周内拿到5份Offer的?

我有个朋友,29岁,功能测试已经2年有余,关于他的职业发展历程,以后会跟大家分享,今天主要想把他跟我分享的面试经历和经验分享给大家,帮助一些迷茫中的朋友。 最近,大概面试了7,8家公…

Redis -List

Redis List 本章介绍redis 的List的数据结构 Redis列表是字符串值的链表。Redis列表经常用于: 1、实现堆栈和队列 2、为后台工作系统提供队列管理 例如: 第一种情况,将List视为一种先进先出的队列 Treat a list like a queue (first in, fi…

Linux系统安装MySQL

使用wget命令下载安装包,命令: ​ wget http://dev.MySQL.com/get/Downloads/MySQL-5.7/mysql-5.7.37-linux-glibc2.12-x86_64.tar.gz​ 1、查看是否已经安装MySQL: 命令: rpm -qa|grep -i mysql 我的Linux是没有安装过的&…

rk3568点亮LCD(mipi)

rk3568 Android11/12 适配 mipi 屏 MIPI (Mobile Industry Processor Interface) 是2003年由ARM, Nokia, ST ,TI等公司成立的一个联盟,目的是把手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性…

【Java版oj】day27不用加减乘除做加法、三角形

目录 一、不用加减乘除做加法 (1)原题再现 (2)问题分析 (3)完整代码 二、三角形 (1)原题再现 (2)问题分析 (3)完整代码 一、不用…

Midjourney V5太炸裂:逼真到颤抖!“有图有真相”时代彻底过去 画画创作者 模特一键淘汰

** ** 文章目录1、Midjourney V51.1、许多人纷纷惊呼:人类画师别活了。1.2、通用模型2、midjourney是什么3、midjourney 的使用方式4、Midjourney新手入门描述词5、一些疑难杂症1、Midjourney V5 继GPT-4发布之后,Midjourney V5上线。网友纷纷试玩&…

java学习之局部内部类

目录 一、内部类简介 二、内部类的分类 三、局部内部类 第一点 第二点 第三点 第四点 第五点 第六点 第七点 一、内部类简介 类的五大成员:属性、方法、构造器、代码块、内部类 package com.hspedu.innerclass;public class InnerClass01 {public static…

2023 年嵌入式世界的3 大趋势分析

目录 大家好,本文讲解了嵌入式发展的3个大趋势,分享给大家。 趋势#1 – Visual Studio Code Integration 趋势#2 –支持“现代”软件流程 趋势 #3 – 在设计中利用 AI 和 ML 结论 大家好,本文讲解了嵌入式发展的3个大趋势,分享…

1、Git使用不完全指南:GitHub的使用详解

GitHub 是一个以开源为基础的社交化编程平台,开发者可以在上面分享代码、协同开发、交流等。下面我们来讲一下如何使用 GitHub。 1. 注册 GitHub 账号 首先,我们需要在 GitHub 上注册一个账号,访问GitHub官网:GitHub: Let’s bui…

IDEA win11安装flutter环境

1.环境说明 操作系统:win11编辑器:Idea 2022.2.1Flutter:3.7.7JDK:17 2.安装 Flutter SDK 2.1安装flutter sdk flutter中文网 2.2配置环境变量 1)在环境变量path中加入flutter的安装路径:D:\DevelopT…

结合PCA降维的DBSCAN聚类方法(附Python代码)

目录 前言介绍: 1、PCA降维: (1)概念解释: (2)实现步骤: (3)优劣相关: 2、DBSCAN聚类: (1)概念解释&a…

关于镜头畸变问题的总结

1、问题背景最近在做的项目有畸变校正的需求,但测试镜头畸变时,发现畸变的形态不太正常。如下图所示中间向内凹、四周向外凸,感觉像是曲线型的。但常见的畸变就两种,一种是向内收的枕形畸变,另一种是向外凸的桶形畸变&…

SpringCloud:ElasticSearch之DSL查询文档

elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括: 查询所有:查询出所有数据,一般测试用。例如…

第04章_IDEA的安装与使用(上)

第04章_IDEA的安装与使用(上) 讲师:尚硅谷-宋红康(江湖人称:康师傅) 官网:http://www.atguigu.com 本章专题与脉络 【Why IDEA ?】 【注】JetBrains官方说明: 尽管我们采取了多种…