【设计模式】Java设计模式——模板方法模式(Template Pattern)

2023-11-14

1. 介绍

1.1 定义

  • 模板方法模式(Template Pattern),又叫模板模式,它属于行为型模式
  • 模板方法模式定义一个模板结构,将具体内容延迟到子类去实现

1.2 作用

  • 使得子类可以在不改变一个模板的结构的前提下重新定义该模板的某些特定步骤

2. 模式结构

2.1 UML类图

在这里插入图片描述

2.2 模式组成

  • 模板方法(Template Method):模板方法定义了模板的特定步骤
  • 抽象方法(Abstract Method):抽象方法由抽象类声明,由其子类实现,使用abstract关键字标识
  • 具体方法(Concrete Method):具体方法由抽象类声明并实现,子类不可修改,使用final关键字标识
  • 钩子方法(Hook Method):钩子方法由抽象类声明,子类可以重写

3. 代码实例

3.1 背景

申请虚拟机需要分配ip,网卡,磁盘等,闲置时需要进行删除,每个操作的请求体各不相同,但是流程是一致的,比如参数校验,资源评估,生成任务id,提交任务等,这个时候定义一个模板类相当合适。

3.2 应用

代码主要为了说明模板方法模式的大致用法,非常简化,仅供参考

  • 步骤1 定义request类(公共参数可以抽成父类)

    public class AbstractVmRequest {
    
        /**
         * create/delete
         */
        private String action;
    
        private String operator;
    
        private String jobId;
    }
    
    public class VmIpRequest extends AbstractVmRequest {
    
        private List<CreateIpVO> createIpVOList;
    
        private List<DeleteIpVO> deleteIpVOList;
    }
    
    public class VmVolumeRequest extends AbstractVmRequest {
    
        private List<CreateVolumeVO> createVolumeVOList;
    
        private List<DeleteVolumeVO> deleteVolumeVOList;
    }
    
  • 步骤2 定义抽象模板类

    public abstract class AbstractVmRegister {
    
        public final String VmRegister(AbstractVmRequest request) throws Exception {
            // 参数校验
            verifyParameter(request);
    
            // 资源评估
            evaluateResource(request);
    
            // 生成任务id
            String jobId = generateJobId(request);
    
            // 提交并执行任务
            submitVmJob(request);
            return jobId;
        }
    
        // 抽象方法,由子类实现
        protected abstract void verifyParameter(AbstractVmRequest request) throws Exception;
    
        // 钩子方法,父类已实现,子类也可以重写
        protected void evaluateResource(AbstractVmRequest request) {
            // 当前资源充足,不进行资源评估
            System.out.println("skip evaluate resource");
        }
    
        // 具体方法,父类已实现,子类无需实现
        final String generateJobId(AbstractVmRequest request) {
            String jobId = UUID.randomUUID().toString();
            request.setJobId(jobId);
            return jobId;
        }
    
        // 抽象方法,由子类实现
        protected abstract void submitVmJob(AbstractVmRequest request);
    }
    
  • 步骤3 定义实现类-ip

    public class VmIpCreateRegister extends AbstractVmRegister {
    
        @Override
        protected void verifyParameter(AbstractVmRequest request) throws Exception {
            VmIpRequest vmIpRequest;
            if (request instanceof VmIpRequest) {
                vmIpRequest = (VmIpRequest) request;
            } else {
                throw new Exception("The bean type does not support ip create");
            }
            if (CollectionUtils.isEmpty(vmIpRequest.getCreateIpVOList())) {
                throw new Exception("createIpVOList can not be empty");
            }
        }
    
        @Override
        protected void submitVmJob(AbstractVmRequest request) {
            // 调用service方法执行操作
            // ipService.execute()
            System.out.println(String.format("submit ip create job, jobId: %s", request.getJobId()));
        }
    }
    
  • 步骤4 定义实现类-volume

    public class VmVolumeCreateRegister extends AbstractVmRegister {
    
        @Override
        protected void verifyParameter(AbstractVmRequest request) throws Exception {
            VmVolumeRequest vmVolumeRequest;
            if (request instanceof VmVolumeRequest) {
                vmVolumeRequest = (VmVolumeRequest) request;
            } else {
                throw new Exception("The bean type does not support volume create");
            }
            if (CollectionUtils.isEmpty(vmVolumeRequest.getCreateVolumeVOList())) {
                throw new Exception("createVolumeVOList can not be empty");
            }
        }
    
        @Override
        protected void submitVmJob(AbstractVmRequest request) {
            // 调用service方法执行操作
            // volumeService.execute()
            System.out.println(String.format("submit volume create job, jobId: %s", request.getJobId()));
        }
    }
    
  • 外部调用(这边推荐使用策略模式,提前将实现类放在map里,调用的时候直接通过map获取实现类)

    public static void main(String[] args) throws Exception {
    
            // 填充参数模拟api调用
            VmVolumeRequest vmVolumeRequest = new VmVolumeRequest();
            // action可以写成枚举类
            vmVolumeRequest.setAction("create");
            vmVolumeRequest.setOperator("DustHeart");
            vmVolumeRequest.setCreateVolumeVOList(Collections.singletonList(new CreateVolumeVO()));
    
            VmVolumeCreateRegister vmVolumeCreateRegister = new VmVolumeCreateRegister();
            String jobId = vmVolumeCreateRegister.VmRegister(vmVolumeRequest);
            System.out.println(jobId);
        }
    

4. 优点

  • 提高代码复用性,将相同处理逻辑的代码放在抽象父类中
  • 提高了拓展性,子类实现抽象父类的某些细节,有助于模板父类的扩展
  • 符合开闭原则,通过一个父类调用子类实现的操作,可以通过子类扩展增加新的行为

5. 缺点

  • 引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,系统会更加庞大
  • 模板方法主要通过继承实现,如果父类增加新的抽象方法,所有的子类都要修改一遍

6. 应用场景

  • 编排一个流程的固定步骤,实现某些步骤不变的部分,并将可变的步骤留给子类来实现,使得子类在步骤固定的情况下进行功能的不同实现和拓展
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【设计模式】Java设计模式——模板方法模式(Template Pattern) 的相关文章

  • 如何使用 UnboundID LDAP SDK 获取 LDAP 中的 DN 和用户 ID

    当我唯一的参数是用户 ID 时 我试图获取用户的 DN 可能不止一个 我还使用 UnboundID LDap SDK 如您所见 public String getCustomerAdminDN String uid String resul
  • 模拟函数指针

    以下类包含一个应使用回调技术计算积分的方法 package integrals import java lang public class Integrals public static double f1 double x return
  • 如何从二维数组中仅打印单个列?

    我正在编写这个程序 我必须只打印二维数组的一列 而不是两者 for int i 0 i lt sjf length i for int j 0 j lt sjf i length j System out printf 5d 4s sjf
  • Zuul不转发请求到其他微服务

    我正在使用 Spring Boot 微服务 我已经配置了 eureka zuul 代理和另一个微服务 帐户 如果我直接从帐户拨打电话 则工作正常 帐户和 zuul 服务器都显示在 eureka 上 当我尝试使用 zuul 代理进行访问时 它
  • Java 中具有级别顺序插入的完整二叉搜索树

    我们接到一个任务 需要编码 二叉搜索树 那个树has to be complete not perfect 这意味着所有不在最低级别或次低级别的节点都应该有 2 个子节点 而最低级别的节点应尽可能远离左侧 我们需要插入到树中等级顺序 所以如
  • Jersey 客户端异步 POST 请求不等待响应

    我创建了一个简单的 Jersey 客户端 它能够成功地使用有效负载执行 POST 请求 但现在它正在等待来自 http 端点的响应 public void callEndpoint String endpoint String payloa
  • 高负载应用程序的数据库可扩展性?

    我见过一些应用程序拥有集群 Web 服务器 例如 10 到 20 个服务器 以具有可扩展性 可以在其中分发 在网络服务器之间加载 但我总是看到所有网络服务器都使用单个数据库 现在考虑任何电子商务或铁路 Web 应用程序 其中有数百万用户在任
  • 从java应用程序发送电子邮件时出现异常:中继被拒绝

    我们正在使用 Spring Mail 从 java 应用程序发送电子邮件 org springframework mail javamail JavaMailSenderImpl Spring电子邮件配置是
  • 创建UML图时应该编写构造函数吗?

    我有一项作业要求我为实际的 Java 程序创建 UML 图 但程序中有几个构造函数方法 我很困惑 我是否应该将这些构造函数方法添加到图中 根据 UML 规范 2 5 版第 11 4 4 节 构造函数是一个具有所属类类型的单个返回结果参数的操
  • CreationException:无法在 Play 2.5.18 中创建注入器错误,以使用 com.google.inject.AbstractModule 替换 GlobalSettings Java 代码

    我正在将 Play 应用程序从 2 5 12 升级到 2 5 18 当我启动该应用程序时 使用sbt 我收到此错误 CreationException 无法创建注入器 看到以下错误 1 Error injecting constructor
  • 有没有办法在@Service上使用@ControllerAdvice

    我有一个项目需求 但我没有任何需求 Controller or RestController但我需要为我的服务层提供一个全局异常处理程序 所以我需要配置 ControllerAdvice on Service 请告诉我是否还有其他方法可以做
  • 在 Retrofit 中的 POST 请求中发送空正文

    我的 api 需要一个空的 json 主体 发出帖子请求时 如何在 Retrofit 和 Jackson 中进行设置 我尝试通过null 和空字符串 以及 但无法让它发挥作用 POST my url Call
  • egit:设置gitignore忽略所有eclipse项目文件

    我在 github 上有一个项目 我想从中删除所有与 eclipse 相关的文件 并允许克隆它的人使用他们想要的任何 ide 这是该项目 https github com vedi0boy Archipelo https github co
  • 为什么对象可以改变类变量的值?

    由甲骨文提供定义 http docs oracle com javase tutorial java javaOO classvars html 有时 您希望拥有所有对象共有的变量 这是通过 static 修饰符来完成的 声明中带有 sta
  • 从 Apache Kafka 中的主题删除消息

    所以我是 Apache Kafka 的新手 我正在尝试创建一个简单的应用程序 以便我可以更好地理解 API 我知道这个问题在这里被问了很多 但是如何清除存储在主题上的消息 记录 我看到的大多数答案都说要更改消息保留时间或删除并重新创建主题
  • 用 Java 编写“漂亮”代码的标准? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • java中的Anagram算法

    我想做字谜算法但是 这段代码不起作用 我的错在哪里 例如 des 和 sed 是字谜 但输出不是字谜 同时我必须使用字符串方法 不是数组 public static boolean isAnagram String s1 String s2
  • Java 需要一个 FileSet 包/类

    任何人都可以建议 Java 中的 FileSet 包 类吗 我所说的 FileSet 是指文件和目录的集合以及正则表达式支持的包含和排除规则 类似于 Apache Ant 谢谢 Apache 公共 IO文件工具 http commons a
  • 从java小程序获取正确的本地IP地址

    我想从我的 java 小程序确定本地 IP 地址 问题是当同一台机器上有多个 IP 地址时 该机器具有 LAN 和互联网连接 掌上电脑 VMWare 这是我的测试 public static void main String args tr
  • Java中不同格式的字符串解析为日期

    我想转换String to Date以不同的格式 例如 我从用户那里得到 String fromDate 19 05 2009 i e dd MM yyyy format 我想转换这个fromDate作为日期对象 yyyy MM dd fo

随机推荐

  • Python:Anaconda安装&常用库(selenium,pymysql)离线安装

    因为网络限制 所以用很多库用pip安装不成功 只能采用离线安装了 方法也简单 按照下面步骤来就好了 1 Anaconda下载安装 下载地址 https www anaconda com products individual 下载后 傻瓜式
  • ES6(这是我见过写的最好的)!推荐

    文章目录 ES6总结 var let const的区别 箭头函数和function的区别 结构赋值 原型 原型链 继承 1 原型链继承 2 构造函数继承 3 组合式继承 4 class类继承 Promise async和await Gene
  • iOS 使用蓝牙耳机的mic作为输入源

    1 首先采样率的设置必须与蓝牙耳机设备的采样率相同 2 然后通过 setPreferredInput 方法从可用的输入设备的数组中选取蓝牙耳机
  • linux应用程序core dump处理

    默认编译出来的程序在出现Segmentation fault 时并没有生成core崩溃文件 可以在gcc g 编译时增加 g选项 如果仍然没有生成core文件 则可能是因为系统设置了core文件大小为0 可以通过 ulimit a 查询得知
  • 实现游戏结束时显示GameOver界面。(Unity)

    在Canvas画板里添加Text文本组件 修改名字为GameOver 修改名字是为了让我们以后更改时更容易找到对应的组件 请名字时尽量和代码一样需要见名知义 并且通过锚点修改他的位置 在位置里修改他需要显示的大小 并且在Text文本组件中修
  • python画笑脸-如何用Python画滑稽笑脸

    Linux编程 点击右侧关注 免费入门到精通 作者丨Saltwater Room https blog csdn net Saltwater Room article details 829 用turtle画滑稽 fromturtle im
  • Android ListView默认选中第一项或第N项

    大体上从查阅的资料和自己的实践一共可以分为以下几种方法 一 重写Adapter 在getView里进行自己的操作 选中 变色等等 class MyAdapter extends BaseAdapter Override public Vie
  • 垂直网络广告

    垂直网站 英文名 Vertical website 注意力集中在某些特定的领域或某种特定的需求 提供有关这个领域或需求的全部深度信息和相关服务 作为互联网的亮点 垂直网站正引起越来越多人的关注 垂直网络广告是指广告发布主体利用网络广告投放平
  • 两个二叉树的合并

    将给定两个二叉树 想象当你将它们中的一个覆盖到另一个上时 两个二叉树的一些节点便会重叠 你需要将他们合并为一个新的二叉树 合并的规则是如果两个节点重叠 那么将他们的值相加作为节点合并后的新值 否则不为 NULL 的节点将直接作为新二叉树的节
  • 手把手教你打造自己的弱口令扫描工具(系统弱口令篇)

    0x01 前言 在渗透测试过程中 弱口令检测是必要的一个环节 选择一个好用的弱口令扫描工具 尤为重要 类似的弱口令检测工具如 Hydra Hscan X Scan 很多时候满足不了自己的需求 通过Python打造自己的弱口令扫描工具 集成在
  • 01FFMPEG的AVFormatContext结构体分析和输出时AVFormatContext的初始化(包含有输入和无输入的AVFormatContext)

    01FFMPEG的AVFormatContext结构体分析和输出时AVFormatContext的初始化 包含有输入和无输入的AVFormatContext 提醒 接下来对所有源码的分析都是针对于目前最新版本的avformat5 8源码 概
  • 五个实施环节

    定级 定级流程 在 信息系统安全等级保护定级指南 中 说明了定级的一般流程 1 确定作为定级对象的信息系统 2 确定业务信息安全受到破坏时所侵害的客体 3 根据不同的受侵害客体 从多个方面综合评定业务信息安全被破坏对客体的侵害程度 4 根据
  • 专项-弱网络测试

    弱网络 简单理解 网络不好 网络环境复杂 使用场景多变 异常逻辑检查 弱网络测什么 测试标准 客户端的核心场景必须有断线重连机制 并在有网络抖动 延时 丢包的网络场景下 客户端需达到以下要求 一 不能出现以下现象 1 游戏中不能出现收支不等
  • Lloyd-Max条件、DPCM系统最佳预测系数推导以及最小二乘:梯度下降、牛顿法、高斯牛顿法总结

    文章目录 1 Lloyd Max条件推导 2 DPCM系统最佳预测系数推导 3 最小二乘 梯度下降 牛顿法 高斯牛顿法总结 3 1 梯度下降 3 2 牛顿法 3 3 高斯牛顿法 1 Lloyd Max条件推导 2 DPCM系统最佳预测系数推
  • Kimera-VIO-ROS,Kimera-Semantic源码运行结果及问题解决

    前期条件准备 1 VMware Ubuntu18 04 2 ROS rviz melodic 3 Eigen3 3 7 一 Kimera VIO ROS 1 源码运行 Polygon mode solid Polygon mode wire
  • vue+springboot中使用ueditor,路由跳转后再进入ueditor,ueditor无法加载出来

    问题描述 已经在标题上了 解决办法 1 在页面销毁时删除ueditor实例 mounted时创建实例 这样做的目的是再次进来时重新加载ueditor mounted 自定义上传路径 const baseUrl http localhost
  • deebot扫地机器人响四声_科沃斯扫地机器人故障声叫4声什么故障_科沃斯扫地机器人故障...

    科沃斯扫地机器人初次使用时 请多多陪同 这样能及时发现问题 解决问题 常见故障二 地宝出现语音提示 运行困难 请检查驱动轮 解决方法 关机后滑动驱动轮 检查其能否转动 回弹 并清理干净 然后把地宝放回原地 点击主机启动键即可继续工作 常见
  • APK的安装流程

    文章目录 我们来思考一下Android系统是如何安装一个APK文件的 从直观的流程上 当我们点击一个APK文件或者从应用商店下载一个APK文件 会弹起一个安装对话框 点击安装就可以安装应用 那么这里面的流程是什么样的呢 首先很容易想到的是
  • QT增加链接库和头文件搜索目录(相对目录)

    QT开发的时候 需要增加链接的动态库或者静态库 或者搜索的头文件 正常情况下 使用相对目录是最好的 下面是常用的方法 1 增加库依赖 如下 OUT PWD表示QT编译后的输出目录 比如Debug或者Release目录 后续发布的时候 把so
  • 【设计模式】Java设计模式——模板方法模式(Template Pattern)

    文章目录 1 介绍 1 1 定义 1 2 作用 2 模式结构 2 1 UML类图 2 2 模式组成 3 代码实例 3 1 背景 3 2 应用 4 优点 5 缺点 6 应用场景 1 介绍 1 1 定义 模板方法模式 Template Patt