当前位置: 首页 > news >正文

【设计模式】之模板方法模式

系列文章目录

  • 【设计模式】之策略模式

  • 【设计模式】之责任链模式

文章目录

系列文章目录

前言

一、什么是模板方法模式

定义

 角色 

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

优点

缺点

三、案例

普通案例

模拟Servlet过程案例

总结


前言

今天给大家介绍23种设计模式中的模板方法模式,这个设计模式在Spring框架中还是挺常见的,手写Spring源码的小伙伴必须要掌握的设计模式之一,文章结构还是经典的三连问,是什么?为什么?怎么用?🌈


一、什么是模板方法模式

定义

模板方法模式法(Template Method)定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

 角色 

  • 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
    • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
    • 基本方法:是实现算法各个步骤的方法。基本方法又可以分为三种:
      • 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
      • 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
      • 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
  • 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

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

优点

  • 代码复用:模板方法将公共的代码放在父类中,子类可以通过继承来复用这部分代码,从而避免在子类中重复编写相同的代码。
  • 算法统一:模板方法确保了算法的整体结构在子类中是一致的,只是某些步骤的实现有所不同。这有助于保持算法的统一性和完整性。
  • 灵活性:模板方法允许子类通过实现抽象方法来定制算法的特定步骤,从而提供了很大的灵活性。
  • 扩展性:如果需要增加新的算法步骤,只需要在父类的模板方法中添加新的抽象方法,并让子类实现这些方法即可,无需修改现有的子类代码。

缺点

  • 增加继承层次:使用模板方法设计模式会增加类的继承层次,这可能会导致系统变得复杂,并增加维护成本。
  • 子类数量过多:如果算法中有很多可变步骤,那么可能需要定义很多抽象方法,从而导致子类数量过多。这会增加系统的复杂性,并可能导致“爆炸式”的子类增长。
  • 违反单一职责原则:有时模板方法可能会包含过多的逻辑,导致父类承担了过多的职责。这违反了单一职责原则,使得父类变得难以维护和扩展。

因此,在使用模板方法设计模式时,需要权衡其优缺点,根据具体场景和需求来决定是否使用以及如何使用该模式。

三、案例

普通案例

炒菜步骤:打开油烟机->生火->炒菜->关火->关闭油烟机,我们就以这个步骤为例,不管炒什么菜都会经过 打开油烟机、生火、关火、关闭油烟机这四个步骤,因此,我们可以将这几个过程定义为一个模板方法,至于炒什么样的菜,让子类去决定。

public abstract class Cook {public void open(){System.out.println("打开抽油烟机");}public void openFire(){System.out.println("生火");}//设置为抽象方法,由每个对象重写这个方法(决定做什么菜)public abstract void doCook();public void closeFire(){System.out.println("关火");}public void close(){System.out.println("关闭抽油烟机");}//做饭模板方法public final void cook(){this.open();this.openFire();this.doCook();this.closeFire();this.close();}
}

子类去继承Cook类实现里面的抽象方法去决定做什么菜。

public class CookPotato extends Cook{@Overridepublic void doCook() {System.out.println("酸辣土豆丝");}
}public class CookTomato extends Cook{@Overridepublic void doCook() {System.out.println("西红柿炒鸡蛋");}
}
 @Testvoid test1(){Cook cookPotato = new CookPotato();cookPotato.cook();System.out.println("==========");Cook cookTomato = new CookTomato();cookTomato.cook();}/*
打开抽油烟机
生火
酸辣土豆丝
关火
关闭抽油烟机
==========
打开抽油烟机
生火
西红柿炒鸡蛋
关火
关闭抽油烟机
*/

模拟Servlet过程案例

我们平常使用的Servlet就是一个典型的模板方法的设计模式。

我们平常使用一般是继承HttpServlet这个类,然后去重写类面的一些方法,如:doGet、doPost、doPut等等

public class testServlet extends MyHttpServlet{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doGet方法");}@Overridepublic void doPost(HttpServletRequest request, HttpServletResponse response) {System.out.println("执行doPost方法");}
}

HttpServlet会继承GenericServlet这个类,HttpServlet会将ServletRequestServletResponse强转为HttpServletRequestHttpServletResponse,并对方法类型进行判断派发。

public abstract class MyHttpServlet extends MyGenericServlet{@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;switch (request.getMethod()){case "GET":this.doGet(request,response);break;case "POST":this.doPost(request,response);break;}}public abstract void doGet(HttpServletRequest request, HttpServletResponse response);public abstract void doPost(HttpServletRequest request, HttpServletResponse response);
}

GenericServlet实现Servlet这个接口,去实现servlet生命周期方法。

public abstract class MyGenericServlet implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {System.out.println("正在初始化");}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException ;@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {System.out.println("正在销毁");}
}

整个大致过程可以总结为:GenericServlet过滤生命周期方法,将service()方法交给HttpServlet去实现,HttpServlet负责将实现具体的方法,最后就得到了我们常见的doGet等方法,我说的这个过程比较简单,其实Servlet整个过程还是挺复杂的,我就抛砖引玉,给大家熟悉下模板方法这个设计模式。


总结

不管哪一种方法模式,都各有利弊,我们平常在开发的过程中,一定要选择最优的解决方式,不可盲目使用。今天的分享就到这里,我们下期再见。✋

http://www.lryc.cn/news/341491.html

相关文章:

  • 【系统架构师】-选择题(十一)
  • 前端开发攻略---介绍HTML中的<dialog>标签,浏览器的原生弹框。
  • 让外贸业绩翻倍的销售话术分享
  • 观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写
  • 虚拟数据中心
  • 解决Blender导出FBX文件到Unity坐标轴错误的问题
  • 基于微信小程序的校园二手闲置物品交易平台的设计与实现
  • java中多线程的3种实现方法
  • 【Docker】docker compose服务编排
  • elementui的el-select+el-tree+el-input实现可搜索的下拉树组件
  • 微信公众号排名 SEO的5个策略
  • python烟花代码
  • Python高级编程
  • leetCode75. 颜色分类
  • 选择器、pxcook软件、盒子模型
  • 商城系统秒杀功能设计思想
  • #初始化列表
  • Vue-组件中的data
  • 抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?
  • 水稻病害检测(YOLO数据集,多分类,稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫)
  • MYSQL数据库专业术语及创建数据表详细讲解{sql语句创建数据库语句及条件子句解析,编码格式解析,创建数据表解析,表定义字段解析,主键约束解析}
  • Kubernetes的13个常用命令,你都熟悉吗
  • python从0开始学习(三)
  • golang判断通道chan是否关闭的2种方式
  • npm install 会报错npm audit错误,会提示你有多少个漏洞需要结局等
  • 揭秘磁盘参数错误:数据恢复的黄金法则与预防策略
  • oracle常见操作
  • SkyWalking 自定义Span并接入告警
  • 代理IP,助力海外社媒运营!
  • 手撕spring框架(3)