模板模式——提炼流程,减少重复开发

2024-03-06 06:40

本文主要是介绍模板模式——提炼流程,减少重复开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

那在这里我先抛出一个我自己总结的暴论:“模板、策略和职责链三个设计模式是解决业务系统流程复杂多变这个痛点的利器”,这三个设计模式应对业务系统的重复开发率高、流程无兜底策略、产品需求无序扩展这几个痛点上非常有用。

什么是模板模式

模版模式,有的也翻译成模版方法模式,主要是因为这个模式里有个模版方法,不过后面实际应用的时候我会提到,这个模版方法在设计一些有客户端和服务多次交互的场景里,其实也可以是虚拟的,我们自己形成意识设计API即可,不一定非要在设计模式的类实现里真实存在。

当要做一件事儿的时候,这件事儿的流程和步骤是固定好的,但是每一个步骤的具体实现方式是不一定的。这个时候就可以使用模板模式

模版模式惯常的用法是,在一个方法模版方法中定义一个算法或者逻辑的流程和步骤,比如先调内部的方法A 再调内部方法B,满足某个条件了不调方法 C 等等,而这个流程中每个步骤对应的方法都可以推迟到子类中去实现,这就给了程序在不改变大流程、步骤的情况下,完成相似性业务的能力。

模版模式实现起来非常简单,用抽象类定义好步骤,提供步骤的默认实现,具体业务逻辑上每个步骤的实现差异交给子类去实现就可以。模版模式的结构用 UML 类图可以这么表示

模板模式用法举例

 比如我们去银行柜台办理业务,存款、取款、购买理财等这些业务的流程中都会有:取号、排位等号、处理业务、服务评价这几个步骤,如果你是金葵花之类的VIP用户,有可能有专属窗口不用排队,检查用户是不是VIP这样步骤叫做钩子方法。

type BankBusinessHandler interface {// 排队拿号TakeRowNumber()// 等位WaitInHead()// 处理具体业务HandleBusiness()// 对服务作出评价Commentate()// 钩子方法,// 用于在流程里判断是不是VIP, 实现类似VIP不用等的需求CheckVipIdentity() bool
}

模板方法,由于 Go 不支持抽象类和子类继承,我们通过类型匿名嵌套来实现,由一个外层类型包装组合BankBusinessHandler接口的实现达到与抽象类和子类继承类似的效果。

type BankBusinessExecutor struct {handler BankBusinessHandler
}// 模板方法,处理银行业务
func (b *BankBusinessExecutor) ExecuteBankBusiness() {// 适用于与客户端单次交互的流程// 如果需要与客户端多次交互才能完成整个流程,// 每次交互的操作去调对应模板里定义的方法就好,并不需要一个调用所有方法的模板方法b.handler.TakeRowNumber()if !b.handler.CheckVipIdentity() {b.handler.WaitInHead()}b.handler.HandleBusiness()b.handler.Commentate()
}

模版模式里:存款、取款与银行客户业务这三者的关系,可以用下面的 UML 图清晰地展示出来:

接下来我们就可以在子类里实现每个银行客户业务的逻辑啦,但是不管哪个业务,都脱离不了取号、等位、办业务、评价服务的大流程。

下面用模板模式实现一下存款业务的流程,代码如下:

type DepositBusinessHandler struct {userVip bool
}// 通用的方法还可以抽象到BaseBusinessHandler里,组合到具体实现类里,减少重复代码(实现类似子类继承抽象类的效果)
func (*DepositBusinessHandler) TakeRowNumber() {fmt.Println("请拿好您的取件码:" + strconv.Itoa(rand.Intn(100)) + " ,注意排队情况,过号后顺延三个安排")
}func (dh *DepositBusinessHandler) WaitInHead() {fmt.Println("排队等号中...")time.Sleep(5 * time.Second)fmt.Println("请去窗口xxx...")
}func (*DepositBusinessHandler) HandleBusiness() {fmt.Println("账户存储很多万人民币...")
}func (dh *DepositBusinessHandler) CheckVipIdentity() bool {return dh.userVip
}func (*DepositBusinessHandler) Commentate() {fmt.Println("请对我的服务作出评价,满意请按0,满意请按0,(~ ̄▽ ̄)~")
}

 执行存款业务的流程则由外部包装类定义的统一模板方法负责发起和调用每个步骤。

func NewBankBusinessExecutor(businessHandler BankBusinessHandler) *BankBusinessExecutor {return &BankBusinessExecutor {handler: businessHandler}
}func main()  {dh := &DepositBusinessHandler{userVip: false}bbe := NewBankBusinessExecutor(dh)bbe.ExecuteBankBusiness()
}

上面实现存款业务流程的时候,我们会发现,像排队取号,等位、服务评价这几个方法,各个银行业务的实现都一样。所以就可以把它们放在抽象类中可以进一步减少代码的重复率。

但是 Go 不是完全面向对象的语言,不过我们可以用类型的匿名嵌套组合来实现相似的效果,把这几个操作的方法交给DefaultBusinessHandler类型实现,再由具体实现类组合它,同样能达到减少重复实现相同逻辑的效果。


type DepositBusinessHandler struct {*DefaultBusinessHandleruserVip bool
}func (*DepositBusinessHandler) HandleBusiness() {fmt.Println("账户存储很多万人民币...")
}func (dh *DepositBusinessHandler) CheckVipIdentity() bool {return dh.userVip
}type DefaultBusinessHandler struct {
}func (*DefaultBusinessHandler) TakeRowNumber() {fmt.Println("请拿好您的取件码:" + strconv.Itoa(rand.Intn(100)) +" ,注意排队情况,过号后顺延三个安排")
}func (dbh *DefaultBusinessHandler) WaitInHead() {fmt.Println("排队等号中...")time.Sleep(5 * time.Second)fmt.Println("请去窗口xxx...")
}func (*DefaultBusinessHandler) Commentate() {fmt.Println("请对我的服务作出评价,满意请按0,满意请按0,(~ ̄▽ ̄)~")
}func (*DefaultBusinessHandler) CheckVipIdentity() bool {// 留给具体实现类实现return false
}func NewBankBusinessExecutor(businessHandler BankBusinessHandler) *BankBusinessExecutor {return &BankBusinessExecutor{handler: businessHandler}
}

注意,上面的DefaultBusinessHandler并没有实现我们想要留给具体子类实现的HandleBusiness方法,这样 DefaultBusinessHandler 就不能算是BankBusinessHandler接口的实现,这么做是为了这个类型只能用于被实现类包装,让 Go 语言的类型检查能够帮我们强制要求,必须用存款或者取款这样子类去实现HandleBusiness方法,整个银行办理业务的流程的程序才能运行起来。

模板模式的使用建议

不一定非要有模版方法

这里,我们例子里这种定义模板方法的方式适用于与客户端单次交互的流程

type BankBusinessExecutor struct {BankBusinessHandler
}// 模板方法,处理银行业务
func (b *BankBusinessExecutor) ExecuteBankBusiness () {b.TakeRowNumber()if !b.CheckVipIdentity() {// VIP 不用等位b.WaitInHead()}b.HandleBusiness()b.Commentate()
}

如果需要与客户端多次交互才能完成整个流程,可以每个交互的操作去使用模板里定义的方法,这个时候,并不需要定义一个调用所有方法的模板方法,这种情况下,也可以理解成,整个流程用到的 RESTful API 接口组合扮演的就是模板方法的角色。

在互联网里C端产品里的典型应用场景,比如:用户经营类的活动API,所有活动都可以抽象成:展示活动信息、奖品信息、判断用户资格、参与活动、抽奖、查看中奖记录、核销奖品这些步骤。那么我们可以利用模板设计模式来对业务流程做抽象,实现各种用户活动都能用一套统一的RESTful API 来支撑业务的效果。

模版与工厂结合使用

还有这里再说一点,在实际开发中,从来没有哪个设计模式是可以独立应用的,更多的时候是几个设计模式联合使用,群策群力、相辅相承来达到项目设计的效果。

而由模版模式把流程的实现逻辑推迟到子类,我们大概也能想到,创建模版子类这个工作交给工厂模式是再合适不过的了,具体使用哪种工厂?一般简单工厂就好,项目刚开始的时候,一般情况下,业务需求和流程我们挖掘的还不够全面,所以一开始的时候不要做太深度的提炼和抽象,等到确实需要的时候再升级到抽象工厂也未尝不可。

模板方法模式的缺点

由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

模板模式这么好,那我们是不是所有流程都要应用上呢?肯定不是,它更适合于经过我们大量实践后,能把某个核心流程提炼成固定步骤的时候再应用。如果提炼得不到位,就得频繁增加或者修改流程里的步骤--也就是修改表示流程的 interface 或者抽象类里的方法。这个时候,如果现有业务中已经存在了多个该流程的实现类的话,那么它们都得做出相应调整才行。

这篇关于模板模式——提炼流程,减少重复开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/779153

相关文章

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

MySQL中删除重复数据SQL的三种写法

《MySQL中删除重复数据SQL的三种写法》:本文主要介绍MySQL中删除重复数据SQL的三种写法,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下... 目录方法一:使用 left join + 子查询删除重复数据(推荐)方法二:创建临时表(需分多步执行,逻辑清晰,但会

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo