在日常的工作生活中,有这些场景:(还有其他生活场景)
- 去银行办理业务一般会经过四个流程:取号、排队、办理业务以及办理完业务后对工作人员进行评分等流程,其中取号、排队和对银行工作人员进行评分的业务对每个顾客都是同样的,可以在父类中实现;但是办理的业务每个人是不同的,取款、存款或者转账等,个人的具体业务可以在子类中实现。
- 一个人每天起床、吃饭、做事及睡觉等,其中做事情因为每个人不同而异,因此我们可以将起床,吃饭,以及睡觉定义为父类,做事定义为子类等
- 简历模板,论文模板,word模板等都可以定义为父类,每个人简历,论文,word模板又不同,可以定义为子类。
以下介绍模板方法模式将解决上述问题。
一 定义
模板方法(Template Method)模式定义为:定义了一个操作中的算法骨架,而将算法的部分步骤延迟到子类,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定的步骤。是一种类行为型模式。
二 特点
模板方法模式的优缺点如下
优点
- 封装不变部分,扩展可变部分。不变部分的算法封装到父类中去实现,而把可变部分算法由子类去继承实现,便于子类扩展。
- 父类中提取了公共的代码部分,便于代码复用。
- 部分的方法是由子类实现,子类拓展相应的功能,符合了开闭原则。
缺点
- 对每个不同的实现都需要一个子类,导致了类的个数增加,系统变得庞大。
- 父类中的抽象方法是由子类实现,子类执行的结果也会影响到父类,提高了代码的阅读难度。
三 模板结构和实现
模板方法模式需要注意具体子类与抽象类之间的协议工作,它用到了虚函数的多态性技术以及反向控制技术。现在介绍其基本结构。
3.1 模板的结构
模板方法模式主要包含以下角色。
3.1.1 抽象类(abstract class)定义了算法的骨架,由一个模板方法和若干个基本的方法来构成。
- 模板方法:定义算法的骨架,按某种顺序调用包含的方法。
- 基本方法:是算法中的一个步骤,包含了以下几种类型。
- 抽象方法:在抽象类中申明,是由子类去实现。
- 具体方法:在抽象类中已经实现,在具体子类中去继承或者重写
- 钩子方法:在抽象类已经实现,包括用于判断的逻辑以及需要子类重写的空方法两种。
拓展:钉子方法
就是在抽象类中定义一个方法,默认不做任何事,子类可以根据实际情况要不要覆盖它,从而改变行为,该方法称为“钩子”。
3.1.2 具体子类(concrete class)实现抽象类中所定义的抽象方法和钩子方法。
结构图如下:
3.2 模板的具体实现
模板方法的模式代码如下
package templateMethod; public class TemplateMethodPattern { public static void main(String[] args) { AbstractClass tm=new ConcreteClass(); tm.TemplateMethod(); } } //抽象类 abstract class AbstractClass { public void TemplateMethod() //模板方法 { SpecificMethod(); abstractMethod1(); abstractMethod2(); } public void SpecificMethod() //具体方法 { System.out.println("抽象类中的具体方法被调用..."); } public abstract void abstractMethod1(); //抽象方法1 public abstract void abstractMethod2(); //抽象方法2 } //具体子类 class ConcreteClass extends AbstractClass { public void abstractMethod1() { System.out.println("抽象方法1的实现被调用..."); } public void abstractMethod2() { System.out.println("抽象方法2的实现被调用..."); } }
程序的运行的结果如下
抽象类中的具体方法被调用... 抽象方法1的实现被调用... 抽象方法2的实现被调用...
四 应用实例
示例
模拟制作咖啡店家接受订单的过程。父类Coffee定义了公共的方法,如研磨咖啡豆,同时定义了可变的部分。但是呢,有的消费者不喜欢加奶,有的不喜欢加糖,所以定义了钩子方法isAddMilk和isAddSugar判断是否要加奶加糖,而子类根据实际需要判断是否要加奶加糖。
Coffee(父类-抽象)
public abstract class Coffee { boolean addSugarFlag = false; boolean addMilkFlag = false; public boolean isAddMilkFlag() { return addMilkFlag; } public boolean isAddSugarFlag() { return addSugarFlag; } Coffee prepareHotWater(){ System.out.println("准备热水"); return this; } Coffee grindCoffeeBean(){ System.out.println("研磨咖啡豆"); return this; } void addSugar(){ System.out.println("加糖"); } void addMilk(){ System.out.println("加奶"); } Coffee make(String coffeeName){ Coffee coffee = prepareHotWater().grindCoffeeBean(); if(isAddMilkFlag()){ coffee.addMilk(); } if(isAddSugarFlag()){ coffee.addSugar(); } System.out.println("制作完成!这是一杯" + (isAddSugarFlag() ? "加" : "不加") + "糖," + (isAddMilkFlag() ? "加" : "不加") + "奶" + "的" + coffeeName); return coffee; } }
Cap类和Latte类
public class Cap extends Coffee { String coffeeName = "卡布奇诺"; Coffee make(){ return super.make(this.coffeeName); } @Override public boolean isAddSugarFlag() { return true; } } public class Latte extends Coffee { String coffeeName = "拿铁"; Coffee make(){ return super.make(this.coffeeName); } @Override public boolean isAddMilkFlag() { return true; } @Override public boolean isAddSugarFlag() { return true; } }
Test类
public class Test { public static void main(String[] args) { System.out.println("****** 下订单:一杯加糖,不加奶的热卡布奇诺 ******"); Cap cappuccino = new Cap(); cappuccino.make(); System.out.println("****** 下订单:一杯加糖,加奶的热拿铁 ******"); Latte latte = new Latte(); latte.make(); } }
输出结果
****** 下订单:一杯加糖,不加奶的热卡布奇诺 ****** 准备热水 研磨咖啡豆 加糖 制作完成!这是一杯加糖,不加奶的卡布奇诺 ****** 下订单:一杯加糖,加奶的热拿铁 ****** 准备热水
五 总结
开发过程中,使用模板方法模式很方便将共有的代码提取出来,能够降低代码的冗余,提高了开发的效率,在开发中应该100%会被用到,或者用到了你却不知道。希望通过本篇博客,大家能够加深对模板方法模式的理解。下一次,我们将讲述设计模式-策略模式,欢迎关注,会实时更新博客!!!