软件设计模式之模板方法模式(JAVA)

什么是模板方法模式?

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

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

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

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

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

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

例如:

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

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

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

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

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

DrinkTemplate.java(模板类)

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

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

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

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

 1 package com.lcw.template.test;
 2
 3 public abstract class DrinkTemplate {
 4
 5     /**抽象基类
 6      *
 7      * 制作饮料方法模板
 8      * 4个步骤    1、烧水 2、冲泡饮料 3、把饮料倒入杯中 4、加调料
 9      * 由于步骤1、3是通用的步骤,适合于制作任何饮料,所以可以把它写死
10      * 2和4步骤,针对不同的饮料有不同的选择,所以可以把它延迟到子类去复写实现(注意访问修饰符)
11      */
12     public final void drinkTempLate(){
13         boilWater();//烧水
14         brew();//冲泡饮料
15         pourInCup();//把饮料倒入杯中
16         addCondiments();//加调料
17     }
18
19     protected abstract void addCondiments();//加调料,由于饮料所加调料各不相同,所以可以延迟到子类实现
20
21     private void pourInCup() {
22         System.out.println("把饮料倒入杯中...");
23     }
24
25     protected abstract void brew();//冲泡饮料 ,由于饮料所用的材料各不相同,所以可以延迟到子类实现
26
27     private void boilWater() {
28         System.out.println("烧水步骤进行中...");
29     }
30 }

MakeCoffee.java(冲泡咖啡类)

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

 1 package com.lcw.template.test;
 2 /**
 3  *
 4  * @author Balla_兔子
 5  * 冲泡咖啡
 6  *
 7  */
 8 public class MakeCoffee extends DrinkTemplate {
 9
10     @Override
11     protected void addCondiments() {
12         System.out.println("加糖...");
13     }
14
15     @Override
16     protected void brew() {
17         System.out.println("加入咖啡粉冲泡...");
18     }
19
20 }

MakeMilkTea.java(冲泡奶茶类)

 1 package com.lcw.template.test;
 2 /**
 3  * 冲泡奶茶
 4  * @author Balla_兔子
 5  *
 6  */
 7 public class MakeMilkTea extends DrinkTemplate {
 8
 9     @Override
10     protected void addCondiments() {
11         System.out.println("加椰果...");
12     }
13
14     @Override
15     protected void brew() {
16         System.out.println("加入奶茶粉冲泡...");
17     }
18
19 }

Test.java(测试类)

 1 package com.lcw.template.test;
 2
 3 public class Test {
 4
 5     /**
 6      * @author Balla_兔子
 7      */
 8     public static void main(String[] args) {
 9         DrinkTemplate coffee=new MakeCoffee();
10         coffee.drinkTempLate();
11         System.out.println("*******************************");
12         DrinkTemplate milkTea=new MakeMilkTea();
13         milkTea.drinkTempLate();
14     }
15
16 }

看下运行效果:

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

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

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

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

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

DrinkTemplate.java(模板类)

 1 package com.lcw.template.test;
 2
 3 public abstract class DrinkTemplate {
 4
 5     /**抽象基类
 6      *
 7      * 制作饮料方法模板
 8      * 4个步骤    1、烧水 2、冲泡饮料 3、把饮料倒入杯中 4、加调料
 9      * 由于步骤1、3是通用的步骤,适合于制作任何饮料,所以可以把它写死
10      * 2和4步骤,针对不同的饮料有不同的选择,所以可以把它延迟到子类去复写实现(注意访问修饰符)
11      */
12     public final void drinkTempLate(){
13         boilWater();//烧水
14         brew();//冲泡饮料
15         pourInCup();//把饮料倒入杯中
16         if(condition()==true){//若条件允许,则加入调料,默认允许
17             addCondiments();//加调料
18         }
19     }
20
21     protected boolean condition() {
22         return true;
23     }
24
25     protected abstract void addCondiments();//加调料,由于饮料所加调料各不相同,所以可以延迟到子类实现
26
27     private void pourInCup() {
28         System.out.println("把饮料倒入杯中...");
29     }
30
31     protected abstract void brew();//冲泡饮料 ,由于饮料所用的材料各不相同,所以可以延迟到子类实现
32
33     private void boilWater() {
34         System.out.println("烧水步骤进行中...");
35     }
36 }

Test.java(测试类)

 1 package com.lcw.template.test;
 2
 3 public class Test {
 4
 5     /**
 6      * @author Balla_兔子
 7      */
 8     public static void main(String[] args) {
 9         DrinkTemplate coffee=new MakeCoffee();
10         coffee.drinkTempLate();
11         System.out.println("咖啡制作完毕!");
12         System.out.println("*******************************");
13         DrinkTemplate milkTea=new MakeMilkTea();
14         milkTea.drinkTempLate();
15         System.out.println("奶茶制作完毕!");
16     }
17
18 }

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

总结下:

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

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

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

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

注意点:

模版方法需要声明成 public final

private方法是基本逻辑

protect abstract 方法是可扩展方法

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

时间: 2024-12-13 21:47:23

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

大话设计模式_模板方法模式(Java代码)

模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 简单描述:多个类的行为是差不多的,只是某些局部不一样,则交由父类中抽象出这些子类中相同的部分,父类中定义出不同的部分的接口(方法),这些不同部分的方法则由子类去实现,通过多态,实现代码的重用 大话设计模式中的截图: 例子代码: AbstractClass类: 1 package com.longsheng.templatemethod; 2 3 public

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

设计模式之第3章-模板方法模式(Java实现) "那个,上次由于我老婆要给我做饭,所以就没有说完就走掉了...这个那个".这次和以前一样,先来开场福利(工厂方法模式已被作者踹下场).由美女抽象工厂介绍一下适用场景~大家欢迎 抽象工厂之应用场景 一个系统要独立于它的产品的创建.组合和表示时. 一个系统要由多个产品系列中的一个来配置时. 当你要强调一系列相关的产品对象的设计以便进行联合使用时. 当你提供一个产品类库,而只想显示它们的接口而不是实现时. “人家要讲的就这么多了,接下来还是让今

设计模式之模板方法模式(Template Method)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式(8)--模板方法模式

关键词 :hook 钩子    Applet 中 init() destory() 为钩子,需要子类去实现. 新原则: 别调用我们,我们会调用你们    高层调用低层   低层不能调用高层. 目的 :封装算法 模板方法:子类决定如何实现算法中的步骤 策略:封装可互换的行为,然后使用委托来决定要采用哪一个行为 工厂方法:由子类决定实例化哪个具体类 OO原则:新原则上面 OO模式:模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类.模板方法使得子类可以在不改变算法的结构的情况下,重

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

模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定的步骤.当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理. AbstractClas是抽象类,其实也就是一抽象模板,定义并实现了一个模板方法.这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现.顶级逻辑也有可能调用一些具体

【大话设计模式】—— 模板方法模式

一.概念 上一篇文章刚刚讲了[大话设计模式]-- 原型模式,原型模式主要是通过Clone()方法,创建新的对象,免去了初始化的过程.模板方法模式也特别会"偷工减料",把不变的行为搬移到超类,去除子类中的重复代码,将代码的复用优势展现的淋漓尽致.它为我们提供了特定的结构和样式,我们只需关心填充数据内容就好,省心啊- 下面让我们来看一下它的准确定义: 模板方法(TemplateMethod)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构

大话设计模式_备忘录模式(Java代码)

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 简单描述:一个Memento类,代表Originator中要备份的属性.Originator负责生成备份和还原备份,CareTaker负责存储备份 大话设计模式中的截图: 例子代码: Memento类: 1 package com.longsheng.memento; 2 3 public class Memento { 4 5 private String sta

大话设计模式_解释器模式(Java代码)

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 简单描述:一个AbstractExpression类,多个子类,存在一个Interpret方法,转义Context对象的信息.客户端根据信息实例化不同的Expression类,并调用其转义方法(这个过程可以使用简单工厂+反射进行) 大话设计模式中的截图: 代码例子: 假设HTML代码解释器: (1)第一类标签<HTML>(开始)/<HEAD>(头信息)/<BODY&g

大话设计模式_原型模式(Java代码)

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 简单描述:即通过实现接口Cloneable重写方法clone(),使得创建新的拷贝对象不需要一个成员一个成员的重新复制,而且可以提高创建对象的效率 Java中要想实现拷贝使用clone()方法,类必须实现Cloneable接口,并且重写Object类中的clone()方法,调用父类的clone()方法即可实现浅复制 代码如下: WorkExperience类: 1 package com.longsheng.prototy