首页 > 编程语言 >模板方法模式:如何实现同一模板框架下的算法扩展?

模板方法模式:如何实现同一模板框架下的算法扩展?

时间:2024-08-30 22:52:35浏览次数:6  
标签:框架 子类 void 算法 步骤 println public 模板

模板方法模式的原理和代码实现都比较简单,在软件开发中也被广泛应用,但是因为使用继承机制,副作用往往盖过了主要作用,所以在使用时尤其要小心谨慎。

一、模式原理分析

模板方法模式原始定义是:在操作中定义算法的框架,将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。

从这个定义中,我们能看出模板方法模式的定位很清楚,就是为了解决算法框架这类特定的问题,同时明确表示需要使用继承的结构

模板方法模式包含关键的两个角色:

  • 抽象父类:定义一个算法所包含的所有步骤,并提供一些通用的方法逻辑。

  • 具体子类:继承自抽象父类,根据需要重写父类提供的算法步骤中的某些步骤。

模板方法模式的代码实现:

public abstract class AbstractClassTemplate {
    void step1(String key){
        //dosomthing
        System.out.println("=== 在模板类里 执行步骤 1");
        if (step2(key)) {
            step3();
        } else {
            step4();
        }
        step5();
    }
    boolean step2(String key){
        System.out.println("=== 在模板类里 执行步骤 2");
        if ("x".equals(key)) {
            return true;
        }
        return false;
    }
    abstract void step3();
    abstract void step4();
    void step5() {
        System.out.println("=== 在模板类里 执行步骤 5");
    }
    void run(String key){
        step1(key);
    }
}
public class ConcreteClassA extends AbstractClassTemplate {
    @Override
    void step3() {
        System.out.println("===在子类 A 中 执行:步骤3");
    }
    @Override
    void step4() {
        System.out.println("===在子类 A 中 执行:步骤4");
    }
}
public class ConcreteClassB extends AbstractClassTemplate {
    @Override
    void step3() {
        System.out.println("===在子类 B 中 执行:步骤3");
    }
    @Override
    void step4() {
        System.out.println("===在子类 B 中 执行:步骤4");
    }
}
public class Demo {
    public static void main(String[] args) {
        AbstractClassTemplate concreteClassA = new ConcreteClassA();
        concreteClassA.run("");
        System.out.println("===========");
        AbstractClassTemplate concreteClassB = new ConcreteClassB();
        concreteClassB.run("x");
    }
}
//输出结果:
=== 在模板类里 执行步骤 1
=== 在模板类里 执行步骤 2
===在子类 A 中 执行:步骤4
=== 在模板类里 执行步骤 5
===========
=== 在模板类里 执行步骤 1
=== 在模板类里 执行步骤 2
===在子类 B 中 执行:步骤3
=== 在模板类里 执行步骤 5

二、为什么使用模板方法模式?

使用模板方法模式的原因主要有两个。

第一个,期望在一个通用的算法或流程框架下进行自定义开发。 比如,在使用 Jenkins 的持续集成发布系统中,可以定制一个固定的 Jenkins Job 任务,将打包、发布、部署的流程作为一个通用的流程。对于不同的系统来说,只需要根据自身的需求增加步骤或删除步骤,就能轻松定制自己的持续发布流程。

第二个,避免同样的代码逻辑进行重复编码。比如,当调用 HTTP 接口时,我们会使用 HttpClient、OkHttp 等工具类进行二次开发,不过我们经常会遇见这样的情况,明明有人已经封装过同样的功能,但还是忍不住想要对这些工具类进行二次封装开发,实际上最终实现的功能都只是调用 HTTP 接口,这样“重复造轮子”会非常浪费开发时间。这时如果使用模板方法模式定义个统一的调用 HTTP 接口的逻辑,就能很好地避免重复编码。

三、使用场景分析

模板方法模式的使用场景一般有:

  • 多个类有相同的方法并且逻辑可以共用时;

  • 将通用的算法或固定流程设计为模板,在每一个具体的子类中再继续优化算法步骤或流程步骤时;

  • 重构超长代码时,发现某一个经常使用的公有方法。

为了更好地理解模板方法模式的使用场景,这里我们通过一个具体的示例来讲解。假设我们正在设计一个简单的持续集成发布系统——研发部开发的代码放在 GitLab 上,并使用一个固定的发布流程来进行程序的上线发布,我们打算使用模板方法模式来定义一系列规范的流程。

首先,我们定义一个通用的流程框架类 DeployFlow,定义的步骤有六步,分别是:从 GitLab 上拉取代码、编译打包、部署测试环境、测试、上传包到线上环境、启动程序。其中,从 GitLab 上拉取代码、编译打包、部署测试环境和测试这四个步骤,需要子类来进行实现,代码如下:

public abstract class DeployFlow {
    //使用final关键字来约束步骤不能轻易修改
    public final void buildFlow() {
        pullCodeFromGitlab(); //从GitLab上拉取代码
        compileAndPackage();  //编译打包
        copyToTestServer();   //部署测试环境
        testing();            //测试
        copyToRemoteServer(); //上传包到线上环境
        startApp();           //启动程序
    }
    public abstract void pullCodeFromGitlab();
    public abstract void compileAndPackage();
    public abstract void copyToTestServer();
    public abstract void testing();
    private void copyToRemoteServer() {
        System.out.println("统一自动上传 启动App包到对应线上服务器");
    }
    private void startApp() {
        System.out.println("统一自动 启动线上App");
    }
}

我们分别实现两个子类:①实现本地的打包编译和上传;②实现全自动化的持续集成式的发布。

public class LocalDeployFlow extends DeployFlow{
    @Override
    public void pullCodeFromGitlab() {
        System.out.println("手动将代码拉取到本地电脑......");
    }
    @Override
    public void compileAndPackage() {
        System.out.println("在本地电脑上手动执行编译打包......");
    }
    @Override
    public void copyToTestServer() {
        System.out.println("手动通过 SSH 上传包到本地的测试服务......");
    }
    @Override
    public void testing() {
        System.out.println("执行手工测试......");
    }
}
public class CicdDeployFlow extends DeployFlow{
    @Override
    public void pullCodeFromGitlab() {
        System.out.println("持续集成服务器将代码拉取到节点服务器上......");
    }
    @Override
    public void compileAndPackage() {
        System.out.println("自动进行编译&打包......");
    }
    @Override
    public void copyToTestServer() {
        System.out.println("自动将包拷贝到测试环境服务器......");
    }
    @Override
    public void testing() {
        System.out.println("执行自动化测试......");
    }
}

我们再来运行一个单元测试,测试一下本地发布 LocalDeployFlow 和持续集成发布 CicdDeployFlow。

public class Client {
    public static void main(String[] args) {
        System.out.println("开始本地手动发布流程======");
        DeployFlow localDeployFlow = new LocalDeployFlow();
        localDeployFlow.buildFlow();
        System.out.println("********************");
        System.out.println("开始 CICD 发布流程======");

        DeployFlow cicdDeployFlow = new CicdDeployFlow();
        cicdDeployFlow.buildFlow();
    }
}
//输出结果
开始本地 手动发布流程======
手动将代码拉取到本地电脑......
在本地电脑上手动执行 编译打包......
手动通过 SSH 上传包 到 本地的测试服务......
执行手工测试......
统一自动上传 启动App包到对应线上服务器
统一自动 启动线上App
********************
开始 CICD 发布流程======
持续集成服务器将代码拉取到节点服务器上......
自动进行编译&打包......
自动将包拷贝到测试环境服务器......
执行 自动化 测试......
统一自动上传 启动App包到对应线上服务器
统一自动 启动线上App

虽然例子比较简单,但是充分展示了模板方法模式的使用方法,也就是需要设置一个父类用于定义整个的算法或流程框架,然后让各自的子类去优化不同的算法步骤细节。

从上面场景的分析中,我们能看出,模板方法模式应用场景的最大特征在于,通常是对算法的特定步骤进行优化,而不是对整个算法进行修改。一旦整体的算法框架被定义完成,子类便无法进行直接修改,因为子类的使用场景直接受到了父类场景的影响

四、模板方法模式的优缺点是什么?

通过上面的分析,我们可以得出使用模板方法模式主要有以下两个优点。

  • 有效去除重复代码。 模板方法模式的父类保存通用的代码逻辑,这样可以让子类不再需要重复处理公用逻辑,只用关注特定的逻辑,从而起到去除子类中重复代码的目的。

  • 有助于找到更通用的模板。 由于子类间重复的代码逻辑都会被抽取到父类中,父类也就慢慢变成了更通用的模板,这样有助于积累更多通用的模板,提升代码复用性和扩展性。

当然,模板方法模式也有一些缺点。

  • 不符合开闭原则。 一个父类调用子类实现操作,通过子类扩展增加新的行为,但是子类执行的结果便会受到父类的影响,不符合开闭原则的“对修改关闭”。

  • 增加代码阅读的难度。 由于父类的某些步骤或方法被延迟到子类执行,那么需要跳转不同的子类阅读代码逻辑,如果子类的数量很多的话,跳转会很多,不方便联系上下文逻辑线索。而且模板方法中的步骤越多,其维护工作就可能会越困难。

  • 违反里氏替换原则。 虽然模板方法模式中的父类会提供通用的实现方法,但是延迟到子类的操作便会变成某种定制化的操作,一旦替换子类,可能会导致父类不可用或整体逻辑发生变化。

模板方法模式是一个比较常用的结构型设计模式,它能帮助我们快速构建一个通用模板,对于固定流程、通用协议而言,模板方法模式都是一个很好的选择。

虽然模板方法模式简单实用有很多优势,但是劣势同样明显。比如,继承的结构容易带来“修改一个类而影响所有类”的情况,另外,阅读代码的体验也不是很好,经常需要各个类之间不断切换,并且要想搞清楚整个算法完整的逻辑,可能需要阅读所有子类和父类的代码逻辑。

总之,模板方法模式的使用范围通常比较局限,当我们的子类和父类在频繁进行修改的时候,就需要重新评估算法步骤是否合理。

文章(专栏)将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发

个人小工具程序上线啦,通过公众号(服务端技术精选)菜单【个人工具】即可体验,欢迎大家体验后提出优化意见

标签:框架,子类,void,算法,步骤,println,public,模板
From: https://blog.51cto.com/jiangyi/11878481

相关文章

  • 【智能算法应用】基于融合改进A星-麻雀搜索算法求解六边形栅格地图路径规划
    目录1.算法原理2.结果展示3.参考文献4.代码获取1.算法原理【智能算法】麻雀搜索算法(SSA)原理及实现六边形栅格地图分析一下地图:六边形栅格地图上移动可以看做6领域运动,偶数列与奇数列移动方式有所差异,将六边形栅格地图与二维栅格地图做映射可以发现:偶数列移动......
  • 机器学习:DBSCAN算法(内有精彩动图)
    目录前言一、DBSCAN算法1.动图展示(图片转载自网络)2.步骤详解3.参数配置二、代码实现1.完整代码2.代码详解1.导入数据2.通过循环确定参数最佳值总结前言        DBSCAN(Density-BasedSpatialClusteringofApplicationswithNoise)是一种基于密度的聚类......
  • 路径规划算法
    FieldD*FiledD*算法是D_starLite算法的一种改进版本,该算法针对基于栅格的路径规划算法通常以栅格端点或中心点作为路径的节点,限制了路径方向变化只能为π/4的倍数,会导致机器人不必要的运动转向,影响执行效率。而DaveFerguson提出的FiledD*算法,通过对栅格进行线性插值使路......
  • 昇思AI框架实践2:基于T5的SQL语句生成模型推理
     MindSpore基于T5的SQL语句生成项目实施基于T5的SQL语句生成项目介绍本项目旨在开发一个基于T5-small模型的自然语言转SQL语句生成器。该生成器能够将用户以自然语言形式提出的查询请求转换为对应的SQL查询语句,从而使得即使是不熟悉SQL语言的用户也能够轻松地从数据库中检......
  • 037.CI4框架CodeIgniter,使用Model模型绑定数据库表
    01、我们创建一个数据库,如下:CREATETABLE`user`(`id`int(20)NOTNULLAUTO_INCREMENT,`username`varchar(255)CHARACTERSETutf8COLLATEutf8_general_ciNULLDEFAULTNULL,`userpassword`varchar(255)CHARACTERSETutf8COLLATEutf8_general_ciNULLDE......
  • TPAMI 2024 | 离散且平衡的谱聚类算法:一种可扩展的方法
    DiscreteandBalancedSpectralClusteringWithScalability离散且平衡的谱聚类算法:一种可扩展的方法RongWang,HuiminChen,YihangLu,QianrongZhang,FeipingNie,andXuelongLi摘要谱聚类(SC)因其卓越的聚类性能而成为深入研究的主要课题。尽管取得了成功......
  • 【有源码】基于Python的猫眼电影数据分析可视化与电影推荐系统K-means算法电影票房数
    注意:该项目只展示部分功能,如需了解,文末咨询即可。本文目录1.开发环境2系统设计2.1设计背景2.2设计内容3系统展示3.1功能展示视频3.2系统页面4更多推荐5部分功能代码1.开发环境开发语言:Python采用技术:K-means算法数据库:MySQL开发环境:PyCharm2系统......
  • 代码随想录算法训练营,8月30日 | 203.移除链表元素, 707.设计链表, 206.反转链表
    链表理论基础1.单链表:数据域和指针域(指向下一个结点的位置)组成,头结点,结尾为空指针;双链表:多了一个指向前一个结点的指针;循环链表:结尾指向头结点。2.链表在内存中的存储不是顺序的,跟数组不同,要找一个数据只能通过前一个数据来找,所有这就导致链表的查询比数组麻烦,但是插入删除数据......
  • 数据结构与算法 第四天(串、数组、广义表)
    串(String)任意字符组成的有限序列串的类型定义串的顺序存储结构模式匹配算法确定主串所含字串第一次出现的位置。BF算法穷举法,从每个字符开始依次匹配KMP算法链式存储数组基本操作特殊矩阵存储对称矩阵三角矩阵对角矩阵稀疏矩阵超过95%元素为零......
  • Vue3+.NET7最新框架实战,手写电商管理后台|2023全新录制,前后分离架构(C#/.NET6/.NET Co
    Vue3+.NET7最新框架实战,手写电商管理后台|2023全新录制,前后分离架构(C#/.NET6/.NETCore)https://pan.baidu.com/s/1SBt4RTT_m6uA9pk857KlcQ?pwd=6666https://www.bilibili.com/video/BV16s4y1m7bd/?spm_id_from=333.337.search-card.all.click&vd_source=e6b56a12a1d9ef11f6c13......