Fork me on GitHub

设计模式之模板方法模式

设计模式之模板方法模式

1. 什么是模板方法模式

Template Method模式也叫模板方法模式,是行为模式之一,它把具有特定步骤算法中的某些必要的处理委让给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为。换句话说就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中去具体实现。

一般的应用场景:

Template Method模式一般应用在具有以下条件的应用中:

  • 具有统一的操作步骤或操作过程
  • 具有不同的操作细节
  • 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同

模板方法模式的结构:

1

  • AbstractClass:抽象类的父类
  • ConcreteClass:具体的实现子类
  • templateMethod():模板方法
  • method1()与method2():具体步骤方法
  • hook()其实AbstractClass里面还应该有hook这个方法,这个方法是在抽象类中实现了,但是可以在继承的子类中进行改动

2. 具体的实例

假设我们是星巴克的师傅,有一个训练手册,我们需要设计冲泡方法就是咖啡的冲泡方法和茶的冲泡方法

咖啡的冲泡方法:烧水,沸水冲泡,咖啡倒进杯子,最后是加糖和牛奶

茶的冲泡方法:烧水,沸水冲泡,茶倒进杯子,最后是加柠檬

对于上面的项目首先我们一开始的设计想法一般是定义两个类分别实现各自的流程呗。但是我们看到这两个类中是有重复的方法的,比如烧水,冲泡,然后我们就想可以把这两个方法拿出来,作为公共的方法。这样的话就又解耦合了一些。最后我们想饮料倒进杯子和加调料是两个抽象的方法定义在一个抽象类中,然后有各自的类去继承和个性化定制。这样我们就完成了整个的设计,这个过程其实就是模板模式的应用。

具体的代码实现:

模板类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class HotDrink {

public final void prepareRecipe(){
boilWater();
brew();
pourIncup();
addCondiments();
}

private void boilWater(){
System.out.println("boil water");
}

public abstract void brew();

private void pourIncup(){
System.out.println("pour in cup");
}

public abstract void addCondiments();

}
1
2
3
4
5
6
7
8
9
10
11
public class Coffee extends HotDrink {
@Override
public void brew() {
System.out.println("brew coffee");
}

@Override
public void addCondiments() {
System.out.println("add sugar and milk");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Tea extends HotDrink {
@Override
public void brew() {
System.out.println("brew tea");
}

@Override
public void addCondiments() {
System.out.println("add lemon");
}
}
1
2
3
4
5
6
7
8
9
public class MainTest {
public static void main(String[] args){
HotDrink drinkTea = new Tea();
drinkTea.prepareRecipe();

HotDrink drinkCoffee = new Coffee();
drinkCoffee.prepareRecipe();
}
}

这样我们就使用模板方法模式完成了设计。

其实还可以通过对模板方法进行挂钩。钩子是一种被声明在抽象类中的方法,但是只有空的或者是默认的实现,其实就是让我们自己来进行个性化扩展的。

对上面的例子我们来进行钩子方式的扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public abstract class HotDrinkWithHook {

public final void prepareRecipe(){
boilWater();
brew();
pourIncup();
if (WantCondimentsHook()){
addCondiments();
}else {
System.out.println("No Condiments");
}

}

public boolean WantCondimentsHook(){
return true;
}

private void boilWater(){
System.out.println("boil water");
}

public abstract void brew();

private void pourIncup(){
System.out.println("pour in cup");
}

public abstract void addCondiments();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class TeaWithHook extends HotDrinkWithHook {
@Override
public void brew() {
System.out.println("brew tea");
}

@Override
public void addCondiments() {
System.out.println("add lemon");
}

@Override
public boolean WantCondimentsHook(){
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Would you like add milk and sugar in your coffee (y/n)");
try{
String str = bufferedReader.readLine();
if (str.equals("y")){
return true;
}else if (str.equals("n")){
return false;
}else {
System.out.println("you have input nothing");
return false;
}
}catch (IOException e){
e.printStackTrace();
}
return false;
}

}

通过上面的实例我们发现其实模板方法模式就是封装了一个算法步骤,并允许子类为一个或多个步骤方法提供实现,模板模式可以使子类在不改变算法结构的情况下,重新定义算法的某些步骤。

其实java中的Comparable接口就是一个模板方法模式设计的,我们可以自己设计个性化方法compareTo()方法。然后使用sort方法就可以实现排序了。

还有一个经典的例子就是Swing的窗口,我们一般写Swing的时候,会使用JFrame,就是那个最基本的Swing容器,使用JFrame的时候我们会继承paint()方法,默认他是不做任何事情的,他就是一个hook(),也就是我们可以自己定制的个性化方法。这里其实就是用的模板方法模式设计的。

3. 好莱坞原则

《Head First 设计模式》上是这样给的定义:你别调用我们,我们会调用你。怎么理解呢?其实就是有的时候高层组件会依赖低层组件,低层组件又依赖高层组件,然后高层组件又依赖边侧组件,边侧组件又依赖低层组件,反正就是乱七八糟的依赖,导致发生了依赖腐败的问题。在好莱坞原则下呢我们允许低层组件挂钩到系统上,但是什么时候使用和怎么使用这些低层组件是由高层组件决定的。换句话就是高层组件对待低层组件的方式就是你别调用我们,我们会调用你。

4. 模板方法模式和策略模式的差异

其实模板方法模式和策略模式还是有点相似的,但是也是有一定的差异的.

模板方法模式是抽象一个子类中的步骤方法,个别的步骤是可以有不同的实现的,而策略模式是将一类方法封装成算法族。

模板方法模式是通过继承实现的,而策略模式是通过对象组合的方式实现的。

模板方法模式的超类是抽象的,而且必须依赖于超类中的方法的实现,而策略模式不依赖于任何人,整个算法可以由他自己搞定。

本文标题:设计模式之模板方法模式

文章作者:WilsonSong

发布时间:2018年10月18日 - 14:10

最后更新:2018年10月18日 - 14:10

原始链接:https://songwell1024.github.io/2018/10/18/TemplateMethod/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------