【什么是设计模式】
官方解释:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
设计模式大咖闫宏解释:模板方法模式是类的行为型模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
【模板类图】
可以看出,模板方法模式由两部分组成:
(1) 抽象类(AbstractClass):抽象类实现了模板方法,定义了算法的框架,其中的方法可以是具体方法也可以是抽象方法(其中抽象方法称为基本操作,算法的框架称为模板方法)。
(2) 具体子类(ConcreteClass):继承抽象类,实现了抽象类中的抽象方法从而完成子类特定的算法,也可以选择是否重写父类中的具体方法。
【模板方法的代码实现】
模板抽象类的实现方法包括三种,分别为:
(1)抽象方法:由抽象类声明,由具体子类实现。
(2)具体方法:具体方法由抽象类声明并实现,而子类并不实现或置换。
(3)钩子方法:钩子方法是由抽象类声明并实现,子类会选择是否应用或是否加以扩展。钩子方法一般有两种实现方式。第一种方式是由抽象类给出空实现,即像下边一样:
- <span style="font-size:14px;background-color: rgb(255, 255, 102);">//定义钩子方法,空实现
- public void printQianYan(){};</span>
- <span style="font-size:14px;">//我们也可以这样使用挂钩,让其决定里面的代码是否执行
- if(hook()){
- //打印前言
- printQianYan();
- }
- @Override
- public boolean hook() {
- return false;
- } </span>
【实例】
模板方法模式应用场景十分广泛。
现实生活中,茶和咖啡是随处可见的饮料。冲泡一杯茶或冲泡一杯咖啡的过程是怎样的?
我们来整理一下流程。
泡茶:
烧开水 ==> 冲泡茶叶 ==> 倒入杯中 ==> 添加柠檬 泡咖啡: 烧开水 ==> 冲泡咖啡 ==> 倒入杯中 ==> 添加糖和牛奶 |
由以上处理步骤不难发现,准备这两种饮料的处理过程非常相似。我们可以使用模板类方法去限定制作饮料的算法框架。
其中相同的具有共性的步骤(如烧开水、倒入杯中),直接在抽象类中给出具体实现。
而对于有差异性的步骤,则在各自的具体类中给出实现。
抽象类
- abstract class Beverage {
- // 模板方法,决定了算法骨架。相当于TemplateMethod()方法
- public void prepareBeverage() {
- boilWater();
- brew();
- pourInCup();
- if (customWantsCondiments())
- {
- addCondiments();
- }
- }
- // 共性操作,直接在抽象类中定义
- public void boilWater() {
- System.out.println("烧开水");
- }
- // 共性操作,直接在抽象类中定义
- public void pourInCup() {
- System.out.println("倒入杯中");
- }
- // 钩子方法,决定某些算法步骤是否挂钩在算法中
- public boolean customWantsCondiments() {
- return true;
- }
- // 特殊操作,在子类中具体实现
- public abstract void brew();
- // 特殊操作,在子类中具体实现
- public abstract void addCondiments();
- }
具体类
- class Tea extends Beverage {
- @Override
- public void brew() {
- System.out.println("冲泡茶叶");
- }
- @Override
- public void addCondiments() {
- System.out.println("添加柠檬");
- }
- }
- class Coffee extends Beverage {
- @Override
- public void brew() {
- System.out.println("冲泡咖啡豆");
- }
- @Override
- public void addCondiments() {
- System.out.println("添加糖和牛奶");
- }
- }
测试类
- public static void main(String[] args) {
- System.out.println("============= 准备茶 =============");
- Beverage tea = new Tea();
- tea.prepareBeverage();
- System.out.println("============= 准备咖啡 =============");
- Beverage coffee = new Coffee();
- coffee.prepareBeverage();
- }
运行结果
- ============= 准备茶 =============
- 烧开水
- 冲泡茶叶
- 倒入杯中
- 添加柠檬
- ============= 准备咖啡 =============
- 烧开水
- 冲泡咖啡豆
- 倒入杯中
- 添加糖和牛奶
【模板方法的优点及应用场景】
容易扩展。一般来说,抽象类中的模版方法是不易反生改变的部分,而抽象方法是容易反生变化的部分,因此通过增加实现类一般可以很容易实现功能的扩展,符合开闭原则。
便于维护。对于模版方法模式来说,正是由于他们的主要逻辑相同,才使用了模版方法,假如不使用模版方法,任由这些相同的代码散乱的分布在不同的类中,维护起来是非常不方便的。
比较灵活。因为有钩子方法,因此,子类的实现也可以影响父类中主逻辑的运行。但是,在灵活的同时,由于子类影响到了父类,违反了里氏替换原则,也会给程序带来风险。这就对抽象类的设计有了更高的要求。
在多个子类拥有相同的方法,并且这些方法逻辑相同时,可以考虑使用模版方法模式。在程序的主框架相同,细节不同的场合下,也比较适合使用这种模式。