设计模式-模版方法模式

news2024/11/22 16:12:40

生活中处处存在模版,模版定义了大的框架,具体内容由使用者填充即可,这给很多人的生活、工作带来了很大的遍历。比如:

  • PPT模版:好的PPT模版提供了更全面的叙述框架,更优美的UI画面&图标,提升用户的PPT制作水平
  • 技术方案模版:通过使用技术方案模版,确定需求应用的各个方面,如需求背景、上下游业务方、存储层设计、接口设计、接口性能评估等。通过完善的模版,技术方案制作时能够考虑的更加全面。

你也可以想象生活中处处可见的其他“模版”,这是一个标准化的过程。在开发技术栈中,spring、mybatic等框架其实也是模版的体现,通过标准化模版式的约定简化开发成本,提升开发效率。因此,我们该怎么通过面向对象语言来体现这种标准化的过程(模版)呢?答案就是模版方法模式。

一、模版方法模式

先思考下实现这种标准化过程的模板类应该具有哪些职责?① 明确指定了标准化过程都有哪些子元素?② 这些子元素在模版中如何组织 。这两点也是我们去评价是否使用这套模版的核心问题,如Python项目不会去考虑使用含有"JVM内存分析"的技术方案模版、产品在写文档时也不会去考虑开发技术方案等。
再考虑下,模板类中“子元素”应该有模板类指定吗?这种具体问题具体分析,有些模版会给个默认示例,有的模版会置之为空,完全由用户来实现。当然,即使是前者,模版肯定也是允许用户重写或覆盖的。所以,这么一说,在代码实现上是否非常适合抽象类呢。
总结下抽象模版类的设计:

  1. 明确了一组标准化过程(或算法框架)-模版方法(templateMethod)
  2. 给出默认或要求用户实现的过程子元素(或步骤)-基本方法([action1(), action2(),…])

抽象模板类结构确定之后,那其具体实现类,即其子类又该注意哪些问题。对于模板类的基本方法而言,实现类必须实现其所有的抽象方法(子类必须实现),非抽象方法视业务要求而定是否需要重写。那问题在于子类(具体实现类)是否应该重写抽象模板类已经确定的模版方法(标准化过程)?我觉得可以通过下面两个方面分析:

  • 模版含义:从模版的含义上来看,其实际是一套标准化过程,具体实现应该尽可能遵守这套流程,才能达到模版效果及预期。同时,结合实际情况看,也是允许具体实现时删除或更改流程顺序,毕竟任何东西都不能是死的要灵活应用。但一定要注意,这里的灵活也仅是小范围的改动,否则,就不用使用这套模版了。
  • 设计原则:如果子类重写了模版方法,更改了标准化流程,子类重写方法与模板类的模版方法业务含义不一致了,因此不满足里氏替换原则。

结合上面两点来看,子类不应该重写模版方法(模版方法通常使用final关键字),但是如何满足具体实现类可控范围内的灵活性呢?子类不允许修改模版方法的逻辑,但是模板类可以向子类提供修改模版方法的能力,即钩子方法。模板类可以通过由子类实现或重写的钩子方法来改变标准化过程。
总结下具体子类的设计:

  1. 必须实现模板类所有的抽象基本方法
  2. 不允许重写模板类中的模版方法,但可通过模板类的钩子方法来控制模版方法的标准化流程。

到目前我们已经可以给出模板类的大致类图如下:
在这里插入图片描述

二、应用实践

模版方法的应用场景非常广泛,只要你能够对一件事情总结出一套标准化的流程,那就可以使用模版方法来实现之。我这里就给出一个开发过程中比较贴近的场景:随着业务的增长需要,服务常会根据职责被拆分为各个单服务,这产生了上下游依赖关系。上下游可通过RPC调用来进行数据交换,这里RPC调用的就是我们通常说的接口。我们试着给出接口对rpc请求大概处理流程:
步骤1:验证接口权限
步骤2:处理请求线程上下文信息
步骤3:解析接口请求参数
步骤4:校验接口请求参数
步骤5:执行具体的接口业务处理操作
步骤6:返回接口响应结果
步骤7:清空上下文信息

注:这里仅是示例,实际上不同业务的接口处理逻辑大多不同,标准化流程也存在差异,即模版之间是会存在差异的。

定义了这样一套标准化流程后,该服务的任何接口均可通过这套流程处理RPC请求。模版及其具体实现代码如下:

/**
 * 处理RPC请求模版类
 */
public abstract class AbstractRPCHandler {
    // 验证接口权限
    protected abstract boolean checkPermission();

    // 处理请求上下文
    protected abstract void processContext();

    // 解析接口请求参数
    protected abstract RequestData parseRequest();

    // 校验接口请求参数
    protected abstract boolean validateRequest(RequestData requestData);

    // 执行具体的接口处理操作
    protected abstract ResponseData process(RequestData requestData);

    // 返回接口响应结果
    protected abstract void sendResponse(ResponseData responseData);

    // 是否清空上下文
    protected boolean toClearContext() {
        return false;
    }

    // 清空上下文
    protected abstract void clearContext();

    // 模板方法,定义rpc接口处理的流程
    public final void processInterface() {
        if (!checkPermission()) {   // 1
            System.out.println("无权限访问接口");
            return;
        }
        processContext();                                   // 2
        RequestData requestData = parseRequest();           // 3
        if (!validateRequest(requestData)) {                // 4
            System.out.println("请求参数校验失败");
            return;
        }

        ResponseData responseData = process(requestData);   // 5
        sendResponse(responseData);                         // 6

        if(toClearContext()) {
            clearContext();                                 // 7
        }
    }
}
/**
 * 处理RPC具体实现类
 */
public class ConcreteRPCHandler extends AbstractRPCHandler{

    @Override
    protected boolean checkPermission() {
        // 验证权限的具体实现...
        return false;
    }

    @Override
    protected void processContext() {
        // 处理上下文的具体实现
    }

    @Override
    protected RequestData parseRequest() {
        // 解析接口请求参数的具体实现
        return null;
    }

    @Override
    protected boolean validateRequest(RequestData requestData) {
        // 校验接口请求参数的具体实现
        return false;
    }

    @Override
    protected ResponseData process(RequestData requestData) {
        // 执行具体的接口处理操作的具体实现
        return null;
    }

    @Override
    protected void sendResponse(ResponseData responseData) {
        // 返回接口响应结果的具体实现
    }

    @Override
    protected void clearContext() {
        // 清空上下文的具体实现
    }
}

在这里插入图片描述

在如上示例中,抽象模版类的clearContext()方法实际上就是钩子方法,用于控制标准化流程processInterface()方法中是否执行清空上下文操作。关于模版方法模式有两个主要的问题:
模版方法模式是否符合开闭原则?从类的扩展角度看,新增具体子类不会影响原有业务逻辑,因此是符合开闭原则的。但是,在一些参考资料中认为模版类中增加基础方法,就需要所有子类跟随修改,所以不符合开闭原则。我在前面的开闭原则讲解中说过,有些改动不能算违背开闭原则,需要看改动背后的业务需要是否本身就对原有业务产生变动了,即需求变动了,代码改动是正常的【那我们得要求产品不改动需求】。因此,我们得先知道“模板类增加基础方法”的背后的业务改动的属性,若属于业务改动,那如前所述,代码改动是符合预期的【除非你跟产品说我不做】。相反,若属于业务扩展,那么扩展的方法似乎又不该放入标准化流程中,因为标准化流程是模板类所负责的职责,职责都变了,那怎么说是业务扩展呢。所以,“模板类增加基础方法”这件事情本身就属于业务改动,就是会影响原有业务代码逻辑-所有子类均需要适配修改。
模版方法模式是否符合里氏替换原则?很多人认为由于模版方法中子类的行为影响了父类的行为,所以不符合开闭原则。这种说法我认为还是没有理解历史替换原则的本质,被很多书籍、定义、条件弄混以至于无法真正理解。我在前面的相关设计原则文章说过这个原则的本质就是子类方法的业务含义必须与基类保持一致或兼容。我们首先看子类实现抽象方法算不算破坏里氏替换原则?如果算的话,那你告诉我该原则还怎么和依赖倒置原则兼容。如果不算,那子类实现的业务逻辑父类可是并没有呀,所以问题不能停留在表面,抽象方法指的是业务含义的定义,约束子类的实现【注:子类实现抽象方法也必须符合抽象定义,否则就是强行违背里氏替换原则】。我们再看模版方法,模版方法是具体业务逻辑还是业务含义?实际上就是业务逻辑,但是这个业务逻辑(标准化流程)是所有子类和父类所共有。这就要求所有子类的标准化流程必须和父类定义的一样,不允许修改,因此模版方法符合里氏替换原则有两个条件:① 模版方法需使用final关键字修饰 ② 不允许有钩子方法。满足这两个条件即满足里氏替换原则,否则就是不满足了。【钩子方法这个条件其实也好理解,子类要是重写了父类方法,那不就是违背了里氏替换原则么,所以确实不能有】
模版方法模式优点:

  • 将标准化流程封装起来供实现类使用,本身就是开闭原则的体现

模版方法模式缺点:

  • 子类的可定制性受限,必须符合标准化流程
  • 采用钩子方法的模版方法模式不符合里氏替换原则,使用多态时会造成不必要的误解,降低代码可读性。

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

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

相关文章

算法训练营第五十一天||309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费 ●总结

309.最佳买卖股票时机含冷冻期 这道题主要就是搞懂dp数组含义以及状态之间的转换&#xff0c;没看答案能自己做出来 class Solution { public:int maxProfit(vector<int>& prices) {vector<vector<int>> dp(prices.size(),vector<int>(5,0));//前…

jMeter使用随记

参数化BodyData 先制作参数文件 再设置一个csv data set config 最后在body data里面写上参数${xxxxx}

【外卖系统】更新员工信息

需求分析 员工管理列表界面&#xff0c;需要对某个员工的账号进行启用和禁用操作。账号禁用的员工不能登录系统&#xff0c;启用后的员工可以正常登录。只有admin可以对其他普通用户进行启用、禁用的操作&#xff0c;普通用户登录系统后启动、禁用按钮都是不显示的编辑员工信息…

家庭有必要买洗地机吗、洗地机排行榜推荐

洗地机相信大家都认识吧&#xff0c;在清洁家电领域这可谓是个“名人”。在清洁工具的名单中&#xff0c;要说一机多用&#xff0c;使用体验好的&#xff0c;洗地机绝对名列前茅。和传统清洁工具相比&#xff0c;洗地机可以很快速的就清洁干净地面&#xff0c;十多分钟就能还你…

数据库—用户权限管理(三十三)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 二、用户权限类型 ​三、用户赋权 四、权限删除 五、用户删除 前言 数据库用户权限管理是指对数据库用户的权限进行控制和管理&#xff0c;确保用户只能执…

探究Spring Bean的六种作用域:了解适用场景和使用方式

这里写目录标题 单例&#xff08;Singleton&#xff09;作用域&#xff1a;原型&#xff08;Prototype&#xff09;作用域&#xff1a;请求&#xff08;Request&#xff09;作用域&#xff1a;会话&#xff08;Session&#xff09;作用域&#xff1a;全局&#xff08;applicati…

【一文搞懂】—带霍尔编码器的直流有刷减速电机

文章目录 一、直流有刷电机二、减速比三、霍尔编码器3.1 霍尔编码器3.2 霍尔编码器测速原理 四、测速程序设计4.1 跳变沿检测4.2 计算转速 一、直流有刷电机 宏观上说直流有刷电机由固定部分&#xff08;定子&#xff09;和旋转部分&#xff08;转子&#xff09;组成。在定子上…

Web Worker的概念、用法、使用场景

​ 目录 1. 简介 2. 适用场景 2.1 复杂计算 2.2 后台下载 2.3 数据处理 2.4 实时通信 3. 代码示例 3.1 Worker特性检测 3.2 Worker API 3.3 SharedWorker API 3.4 创建 JavaScript 文件 3.5 创建 Web Worker 4. 总结 1. 简介 Web Worker 使得在一个独立于 Web 应…

2023-07-27 LeetCode每日一题(删除每行中的最大值)

2023-07-27每日一题 一、题目编号 2500. 删除每行中的最大值二、题目链接 点击跳转到题目位置 三、题目描述 给你一个 m x n 大小的矩阵 grid &#xff0c;由若干正整数组成。 执行下述操作&#xff0c;直到 grid 变为空矩阵&#xff1a; 从每一行删除值最大的元素。如果…

VS2022和QT混合编程打包发布程序

1.在开始菜单输入 CMD 找到 Qt5.15.2(MSVC 64-bit) 2.输入windeployqt exe所在路径 3.运行完毕后&#xff0c;双击打开exe文件&#xff0c;可能会报错&#xff0c;缺少相关的dll,找到缺少的dll拷贝到运行文件夹下即可。

数字化管理能给企业带来哪些好处?

企业数字化管理&#xff08;EDM&#xff09;是指使用数字技术和工具来管理企业运营和流程的各个方面。如果有效实施&#xff0c;EDM 可以给企业带来多种好处&#xff0c;提高企业的整体效率、生产力和竞争力。以下是一些主要优点&#xff1a; 1.提高效率&#xff1a;EDM 通过自…

参数自定义配置比例阀放大器

模拟指令输入比例阀放大器通常使用模拟信号来控制其输出&#xff0c;例如10V, 0~5V,0~10V,4~20mA模拟量信号。它可以将输入的模拟信号放大并转换为一个与输入信号成正比的输出信号&#xff0c;从而实现对执行机构的位置或速度控制。 适配各种不带位置反馈比例阀的控制&#xf…

Python 并发编程 Futures

文章目录 说明1. 并发与并行2. Futures 模块2.1 顺序执行2.2 并发执行2.3 并行执行2.4 Executor 对象 3. 全局解释器锁3.1 为什么有 GIL3.2 GIL 工作过程3.3 线程安全 后记 说明 编程中如果能合理利用编程语言的并发编程技巧&#xff0c;都可以极大提升程序的性能。在 Python …

手把手教你激活虹科物联网HMI/网关数据库功能

前言 JMobile Studio 4.5的更新使得虹科物联网HMI/网关可以本地支持MySQL、PostgreSQL以及支持ODBC驱动连接的数据库&#xff0c;实现设备数据的存储&#xff0c;方便企业数据的统筹管理。 因此&#xff0c;本文主要介绍如何激活虹科物联网HMI/网关的数据库功能。 操作步骤 …

淘宝订单截图生成器网页版制作

你是否曾经为手动制作淘宝订单截图而烦恼&#xff1f;现在&#xff0c;有了淘宝订单生成器&#xff0c;这一切都变得轻松起来。 作为一款专为淘宝购物爱好者打造的神器&#xff0c;淘宝订单生成器可以轻松帮你生成美观的订单截图&#xff0c;让你的朋友们羡慕不已。不再需要手…

临床数据 4. 肿瘤克隆进化分析结果解读?

临床数据分析方案 桓峰基因公众号推出临床数据分析方案&#xff0c;整理如下&#xff1a; 临床数据 1. 临床基因突变数据如何发高分&#xff1f; 临床数据 2. 基于NGS的胃癌诊疗全过程的临床应用方案 临床数据 3. 肿瘤微小残留病灶(MRD)如何发文章&#xff1f; 克隆进化生信分析…

【雕爷学编程】Arduino动手做(93)--- 0.96寸OLED液晶屏模块11

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

UI设计工具都有哪些好用的推荐?

对于UI设计的初学者来说&#xff0c;掌握一个实用且易于使用的界面UI软件是非常重要的。今天&#xff0c;我整理了四个易于使用的界面UI软件。让我们看看。 即时设计 即时设计是一款免费的在线 UI 设计工具&#xff0c;无系统限制&#xff0c;浏览器打开即可使用&#xff0c;…

gin框架内容(二)

上一篇过于gin的内容 https://mp.csdn.net/mp_blog/creation/editor/131953861 CSDNhttps://mp.csdn.net/mp_blog/creation/editor/131953861 一、路由组 为了管理具有相同前缀的URL, 将拥有URL共同前缀的路由划分为一组 为了代码的阅读性&#xff0c;使用{}包裹相同组的路由…

SDN系统方法 | 7. 叶棘网络

随着互联网和数据中心流量的爆炸式增长&#xff0c;SDN已经逐步取代静态路由交换设备成为构建网络的主流方式&#xff0c;本系列是免费电子书《Software-Defined Networks: A Systems Approach》的中文版&#xff0c;完整介绍了SDN的概念、原理、架构和实现方式。原文: Softwar…