什么是模板方法模式?

定义一个操作中算法的骨架,而将这些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

好抽象的概念啊,文绉绉的东西就是不讨人喜欢,下面我用一个生活中常见的例子来举例说明吧

上图是个饮料机,大家都很熟悉吧,各大商场随处可见的一个东西,这里举个例子,比如我们想喝某种饮料,只要按下对应的饮料类别,饮料就自动出来了。

这里我们可以抽象化的想象下饮料在机器里的制作过程(这里只是简单举例,别钻牛角尖哈)

大致我们可以分成4个步骤

①烧水  ②冲泡饮料  ③把饮料倒入杯中  ④加入调料

例如:

咖啡:烧开热水-->加入咖啡粉冲泡-->把饮料倒入杯中-->加入少许糖

奶茶:烧开热水-->加入奶茶粉冲泡-->把饮料加入杯中-->加入椰果/珍珠

不难发现,饮料制作过程中的步骤中的①烧水、③把饮料倒入杯中是重复工作,制泡哪种饮料都一样,那么也就是重复工作,我们可以把它设定为通用性操作。

我们只需要去关心步骤②和步骤④即可

由于制泡饮料的步骤就是这4步,所以我们可以把它抽象成一个"制作饮料模板"出来,下面就以上面这个例子,我用代码来说明

DrinkTemplate.java(模板类)

这是一个制作饮料的模板类,也就是制作所有饮料的基类

我们可以把这4个步骤封装到一个模板方法里,并实现里面的通用步骤

由于避免继承它的子类去修改整体制作架构,所以这个方法用了final修饰符来修饰,好比著名的"好莱坞原则":Don't call us, we'll call you 子类需要听从父类的安排

由于步骤②和步骤④需要根据具体制泡的饮料来确定,所以需要延迟到子类去实现,这里采用了protected修饰符以便子类可以复写,其他方法就可以直接写"死"掉,用private修饰符修饰,这样的使得代码工作人员能够更加关注自身的工作,而不必去考虑一些其他因素。

 package com.lcw.template.test;

 public abstract class DrinkTemplate {

     /**抽象基类
*
* 制作饮料方法模板
* 4个步骤 1、烧水 2、冲泡饮料 3、把饮料倒入杯中 4、加调料
* 由于步骤1、3是通用的步骤,适合于制作任何饮料,所以可以把它写死
* 2和4步骤,针对不同的饮料有不同的选择,所以可以把它延迟到子类去复写实现(注意访问修饰符)
*/
public final void drinkTempLate(){
boilWater();//烧水
brew();//冲泡饮料
pourInCup();//把饮料倒入杯中
addCondiments();//加调料
} protected abstract void addCondiments();//加调料,由于饮料所加调料各不相同,所以可以延迟到子类实现 private void pourInCup() {
System.out.println("把饮料倒入杯中...");
} protected abstract void brew();//冲泡饮料 ,由于饮料所用的材料各不相同,所以可以延迟到子类实现 private void boilWater() {
System.out.println("烧水步骤进行中...");
}
}

MakeCoffee.java(冲泡咖啡类)

这个没啥好说的,就是继承了抽象基类,并复写了它的抽象方法

 package com.lcw.template.test;
/**
*
* @author Balla_兔子
* 冲泡咖啡
*
*/
public class MakeCoffee extends DrinkTemplate { @Override
protected void addCondiments() {
System.out.println("加糖...");
} @Override
protected void brew() {
System.out.println("加入咖啡粉冲泡...");
} }

MakeMilkTea.java(冲泡奶茶类)

 package com.lcw.template.test;
/**
* 冲泡奶茶
* @author Balla_兔子
*
*/
public class MakeMilkTea extends DrinkTemplate { @Override
protected void addCondiments() {
System.out.println("加椰果...");
} @Override
protected void brew() {
System.out.println("加入奶茶粉冲泡...");
} }

Test.java(测试类)

 package com.lcw.template.test;

 public class Test {

     /**
* @author Balla_兔子
*/
public static void main(String[] args) {
DrinkTemplate coffee=new MakeCoffee();
coffee.drinkTempLate();
System.out.println("*******************************");
DrinkTemplate milkTea=new MakeMilkTea();
milkTea.drinkTempLate();
} }

看下运行效果:

哈哈,这样的实现类写起来是不是很清晰明了啊,只需要去复写我们需要关心的方法即可,大大提高了代码的复用性。

但这里有个问题就暴露出来了,冲泡咖啡的实现固然没错,但总有些人喝咖啡是不加糖的,这是该怎么办呢?

这里就引入了一个"钩子"hook概念

我们可以在某个具体实现方法前后分别加入钩子,就好比是前置方法或者后置方法,就像日志技术一样,在每完成一个业务动作前都需要记录日志

而这个前置方法,我们可以利用一个布尔来做判断,并给它一个默认,来看看具体实现方法

DrinkTemplate.java(模板类)

 package com.lcw.template.test;

 public abstract class DrinkTemplate {

     /**抽象基类
*
* 制作饮料方法模板
* 4个步骤 1、烧水 2、冲泡饮料 3、把饮料倒入杯中 4、加调料
* 由于步骤1、3是通用的步骤,适合于制作任何饮料,所以可以把它写死
* 2和4步骤,针对不同的饮料有不同的选择,所以可以把它延迟到子类去复写实现(注意访问修饰符)
*/
public final void drinkTempLate(){
boilWater();//烧水
brew();//冲泡饮料
pourInCup();//把饮料倒入杯中
if(condition()==true){//若条件允许,则加入调料,默认允许
addCondiments();//加调料
}
} protected boolean condition() {
return true;
} protected abstract void addCondiments();//加调料,由于饮料所加调料各不相同,所以可以延迟到子类实现 private void pourInCup() {
System.out.println("把饮料倒入杯中...");
} protected abstract void brew();//冲泡饮料 ,由于饮料所用的材料各不相同,所以可以延迟到子类实现 private void boilWater() {
System.out.println("烧水步骤进行中...");
}
}

Test.java(测试类)

 package com.lcw.template.test;

 public class Test {

     /**
* @author Balla_兔子
*/
public static void main(String[] args) {
DrinkTemplate coffee=new MakeCoffee();
coffee.drinkTempLate();
System.out.println("咖啡制作完毕!");
System.out.println("*******************************");
DrinkTemplate milkTea=new MakeMilkTea();
milkTea.drinkTempLate();
System.out.println("奶茶制作完毕!");
} }

看下这次的效果,哈哈,无糖咖啡出炉~

总结下:

先说说模板方法模式的优点:

1、封装性好  2、复用性好、  3、屏蔽细节  4、便于维护

至于缺点呢,就是继承问题,在JAVA里只能继承一个父类。

流程: 分析场景-->步骤抽取-->重构代码-->重要、复杂的算法,核心算法设计为模版

注意点:

模版方法需要声明成 public final

private方法是基本逻辑

protect abstract 方法是可扩展方法

钩子使得模板方法更加灵活

软件设计模式之模板方法模式(JAVA)的更多相关文章

  1. 设计模式之模板方法模式(Java实现)

    "那个,上次由于我老婆要给我做饭,所以就没有说完就走掉了...这个那个".这次和以前一样,先来开场福利(工厂方法模式已被作者踹下场).由美女抽象工厂介绍一下适用场景~大家欢迎 抽象 ...

  2. 软件设计模式之代理模式(JAVA)

    貌似停笔了近半个月了,实在不该啊,新的一年,时刻让自己归零. Back To Zero,就从这篇文章拉开今年的序幕吧. 这篇文章准备介绍下有关代理模式的基本概念和静态代理.动态代理的优缺点及使用方法( ...

  3. 软件设计模式之工厂模式(JAVA)

    什么是工厂模式? 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式.著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见.因为工厂模式就相 ...

  4. 折腾Java设计模式之模板方法模式

    博客原文地址:折腾Java设计模式之模板方法模式 模板方法模式 Define the skeleton of an algorithm in an operation, deferring some ...

  5. 设计模式之第3章-模板方法模式(Java实现)

    设计模式之第3章-模板方法模式(Java实现) "那个,上次由于我老婆要给我做饭,所以就没有说完就走掉了...这个那个".这次和以前一样,先来开场福利(工厂方法模式已被作者踹下场) ...

  6. 乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern)

    原文:乐在其中设计模式(C#) - 模板方法模式(Template Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 模板方法模式(Template Method ...

  7. js设计模式——6.模板方法模式与职责链模式

    js设计模式——6.模板方法模式与职责链模式 职责链模式

  8. Java设计模式应用——模板方法模式

    所谓模板方法模式,就是在一组方法结构一致,只有部分逻辑不一样时,使用抽象类制作一个逻辑模板,具体是实现类仅仅实现特殊逻辑就行了.类似科举制度八股文,文章结构相同,仅仅具体语句有差异,我们只需要按照八股 ...

  9. Java设计模式之模板方法模式(Template)

    前言: 我们在开发中有很多固定的流程,这些流程有很多步凑是固定的,比如JDBC中获取连接,关闭连接这些流程是固定不变的,变动的只有设置参数,解析结果集这些是根据不同的实体对象“来做调整”,针对这种拥有 ...

随机推荐

  1. NLP自然语言处理 jieba中文分词,关键词提取,词性标注,并行分词,起止位置,文本挖掘,NLP WordEmbedding的概念和实现

    1. NLP 走近自然语言处理 概念 Natural Language Processing/Understanding,自然语言处理/理解 日常对话.办公写作.上网浏览 希望机器能像人一样去理解,以 ...

  2. 解决Eclipse异常关闭后重启报 org.eclipse.swt.SWTException: Invalid thread access 的问题

    . . . . . 很久没有写博客了,最近实在是太忙,一直想写点干货,但是一直没静下心来学习. 今天又在加班忙碌之中,结果谁知道越忙碌越出问题.先是 weblogic 没有正常启动,凭经验第一反应就是 ...

  3. Thinkphp CURD中的where方法

    今天来给大家讲下查询最常用但也是最复杂的where方法,where方法也属于模型类的连贯操作方法之一,主要用于查询和操作条件的设置.where方法的用法是ThinkPHP查询语言的精髓,也是Think ...

  4. MyBatis 之 SqlSessionManager 源码分析

    MyBatis 的 4 个基本构成: SqlSessionFactoryBuilder(构造器): 根据配置信息或者代码来生成 SqlSessionFactory(工厂接口) SqlSessionFa ...

  5. C语言 · 排列数 · 排列式

    蓝桥练习场上不断碰到类似的题,都是一个递归搜索的套路. 算法提高 排列数   时间限制:1.0s   内存限制:256.0MB      问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下 ...

  6. am335x SPI spi_d0, spi_d1 out, in 模式设定

    AM335x SPI DO DI 的模式 参考: https://www.deyisupport.com/question_answer/dsp_arm/sitara_arm/f/25/t/13826 ...

  7. redis详细配置文件

    redis 单机版自己指定配置 #修改为守护模式 daemonize yes #设置进程锁文件 pidfile /usr/local/redis/redis.pid #端口 port 6379 #客户 ...

  8. C# BCC异或校验法

    通讯协议中最后一位是校验位 采用BCC(异或校验)法 这个怎么解释? 就是 把16进制的字符串转换成byte[]数据,依次拿出来进行异或一下 然后返回的值和校验位对比下 看看对不对 例如: 23230 ...

  9. PCL中可用的PointT类型

    PCL中可用的PointT类型: PointXYZ——成员变量:float x,y,z; PointXYZ是使用最常见的一个点数据类型,因为他之包含三维XYZ坐标信息,这三个浮点数附加一个浮点数来满足 ...

  10. JQuery操作cookies

    jQuery.cookie = function(name, value, options) { if (typeof value != 'undefined') { // name and valu ...