当前位置: 首页 > 科技观察

设计模式的模板方法模式

时间:2023-03-19 09:51:41 科技观察

你会发现每个公司都有一个规范,比如请假流程规范,代码规范等,每个公司都有这个流程,只是具体执行条件不一样。设计模式中的模板方法模式也可以理解为规范模板。主要是为了提高我们代码的复用性,以及扩展性等问题。这种模板方法也可以在我们当舔狗的时候跟姐妹们聊天的时候用到。比如这样一个模板:“宝,XXXX,XXXX什么XX?X你的XXX”当我得到这样一个模板时,我可以举一反三,直接套用。我们直接填参数就可以了,比如:“宝,我打过疫苗,我打的是什么疫苗,我爱你的每一分每一秒”“宝,我做了核酸,我怎么办?酸,我拿不下你的心痛”“宝,我今天去输液了,输了什么液,晚上好想你”……好了,言归正传,框架中的模板方法模式也很常见。今天我们就来说说设计模式中行为设计模式中的模板方法模式。设计模式系列前几篇文章:单例模式工厂模式流程引擎构建器模式原型模式链责任模式观察者模式策略模式大纲或老规矩我们从上图中的五个方面来详细说说模板方法。模式定义模板方法模式的定义和目的?定义:模板方法模式在方法中定义了一个算法框架,并将某些步骤推迟到子类。模板方法模式允许子类在不改变算法整体结构的情况下重新定义算法中的某些步骤目的:1.使用模板方法模式的目的是避免编写重复的代码,让开发者可以专注于核心业务逻辑2.解决接口与接口实现类的继承矛盾问题。以上定义来自《设计模式之美》结构图:AbstractTemplate(抽象模板):定义了一系列抽象方法,或者实现方法,或者hook方法。即:定义流程ConcreteTemplate(具体模板):实现父类的抽象方法,根据自身不同的模板业务逻辑实现不同的业务逻辑代码。即:抽象方法的实现是一样的,只是内部逻辑不同。整个结构图看起来还是很简单的,但是还是要明白设计模式解决的是什么问题。代码实现?让我们举个例子。我们以上面的请假申请为例。假设A公司需要直接主管的批准,并通知HR有人请假。B公司需要直属上司,部门负责人审批,最后通知HR,完成整个请假流程。那么作为OA办公流程如何处理这个问题呢?直接看代码实现!publicabstractclassAskForLeaveFlow{//一级组长直接批protectedabstractvoidfirstGroupLeader(Stringname);//二级组长批protectedvoidsecondGroupLeader(Stringname){}//通知HR有人请假privatefinalvoidnotifyHr(Stringname){System.out.println("当前有人请假,请假人:"+name);}//请假流程模板publicvoidaskForLeave(Stringname){firstGroupLeader(name);secondGroupLeader(name);notifyHr(name);}}all,定义一个请假流程,其中:firstGroupLeader方法抽象修饰,然后作为子类,必须实现secondGroupLeader用于二级领导审批,可以在子类中重写,或者不重写notifyHr方法通知HR最后askForLeave请假流程方法内部已经实现了,上面的模板方法是连在一起的publicclassCompanyAextendsAskForLeaveFlow{@OverrideprotectedvoidfirstGroupLeader(Stringname){System.out.println("CompanyA组有人请假,请假人:"+姓名);}}publicclassCompanyBextendsAskForLeaveFlow{@OverrideprotectedvoidfirstGroupLeader(Stringname){System.out.println("有人在A组B公司组请假,请假人:"+name);}@OverrideprotectedvoidsecondGroupLeader(Stringname){System.out.println("B公司部门有人请假,请假人:"+name);}}在CompanyA和CompanyB中,secondGroupLeader二级领导可以选择是否重写,该类模板方法称为钩子方法publicclasstestTemplate{publicstaticvoidmain(String[]args){//公司A请假流程模板AskForLeaveFlowcompanyA=newCompanyA();companyA.askForLeave("敖丙");//结果:CompanyA组有人请假,请假人:敖丙//当前有人请假,请假人:敖丙AskForLeaveFlowcompanyB=newCompanyB();companyB.askForLeave("敖丙");//结果:CompanyB组有人请假,请假人:敖丙//CompanyB部门有人请假,请假人:敖丙//目前有人请假,请假人:敖丙}}最后是看testdome结果。公司A和公司B分别输出相应的请假流程。细心的同学可能已经发现,除了抽象方法,模板方法中还可以有具体的实现方法和钩子方法。所以,在应用的过程中,可以多思考一下,是不是在内部定义模板方法,是应该定义成抽象方法还是别的什么。框架中的应用模板方法模式在我们常见的Java框架中也很常见,只是我们可能没有注意到而已。第一种:首先,我们在学习SpringMVC的时候,一开始都会写一些Servlets来处理一些post或者get请求。这里直接看源码可以发现,这也是直接使用模板方法模式的思路。期间,HttpServlet继承GenericServlet也是模板方法的体现,可见模板可以多次抽象。第二个:在文件流常见问题中,在JavaIO类中的InputStream、OutputStream、Reader、Writer等可以看到模板方法模式。以上是我贴出来的InputStream的部分源码。主要依赖于读取模板方法,是模板方法模式的体现。当然IO类还有很多,就不一一贴出源码了。有兴趣的同学可以打开源码了解。业务实例如何在业务中使用模板方法?首先,你需要了解模板方法,它的存在是为了增加代码的可重用性和可扩展性,所以我将基于这个想法给你举个例子。之前写过责任链模型,最后给大家举个产品细节的例子。这次还是用productdetails,但是用模板方法模型来实现这个问题,理解为2.0版本的businessdetails。商品详情展示可以分模块展示,比如头图、商品信息、sku信息、收货地址、分期付款等,那么如何组装展示商品详情呢?流程图:可以看到有一个请求来了,模块组装器可以选择组装并返回结果。提一点,为了减少第二步请求模块时整个链路的请求时间,可以考虑串行或者并行(开启线程池处理)。接下来直接看代码publicabstractclassAbstractTemplateBlock{//组装结果publicTtemplate(ModelContainermodelContainer){Tblock=initBlock();try{this.doWork(modelContainer,block);}catch(Exceptione){//可以选择catchexceptions,是否打断进程,或者只打印日志,不打断进程}returnblock;}//初始化构建返回结果模型protectedabstractTinitBlock();//定义抽象模板protectedabstractvoiddoWork(ModelContainermodelContainer,Tblock)throwsException;}或者先创建模板Block@ComponentpublicclassItemInfoBlockextendsAbstractTemplateBlock{@OverrideprotectedItemInfoBlock.ItemInfoinitBlock(){returnnewItemInfoBlock.ItemInfo();}//模拟业务逻辑,组装并返回产品信息模块数据@OverrideprotectedvoiddoWork(ModelContainermodelContainer,ItemInfoblock)throws{block.set3Item)Id(“测试”);}@DatapublicstaticclassItemInfo{privateLongitemId;privateStringitemName;}}这里只写了一个ItemInfoBlock,其他模块也是这样写的,就不写全了。publicstaticvoidmain(String[]args){//1.模拟获取SpringBeanApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");ItemInfoBlockitemInfoBlock=(ItemInfoBlock)applicationContext.getBean("itemInfoBlock");//2.ModelContainer可以理解为在上下文中通过Request参数运行,或者预加载一些组装数据所需的数据ModelContainermodelContainer=newModelContainer();//3.得到返回结果ItemInfoBlock.ItemInfoitemInfo=itemInfoBlock.template(modelContainer);System.out.println(JSON.toJSONString(itemInfo));//result:{"itemId":123,"itemName":"test"}}最后,看测试demo。可以看到每个模块中都有一个AbstractTemplateBlock,里面包含了doWork抽象方法。由子类来实现当前的业务逻辑。同时,在第三步获取返回结果时,我只是单独列出来,大家可以根据实际情况进行修改。比如返回map结构等,mapKey是模块名,value是数据。目前拼装产品细节的模式也是比较普遍的方式。代码的复用性高,可扩展性也在一定程度上体现出来,符合模板方法模式的思想。总结一下模板方法模式的特点,大家应该能明白适用场景是增加代码的复用性和可扩展性。说起来还是有道理的,那句话的存在,写代码的时候不要因为设计模式而强行嵌套。合理了解每种设计模式适合的场景,解决什么问题。