设计模式--模板设计模式

模板设计模式—基于抽象类的,核心是封装算法

  • 模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体实现

  • 模板(模板方法)模式(Servlet、AQS)

    在一个方法中定义一个算法的骨架,并将一些具体步骤延迟到子类中实现。 模板模式使得子类可以在不改变算法结构的基础上,重新具体定义算法中的某些步骤

讲模板设计模式之前,我们用代码来实现咖啡和茶制作的类:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Coffee {    
  /*  
    * 咖啡冲泡法(算法)    
  */    
  void prepareRecipe() {        
      boilWater();        
      brewCoffeeGrings();        
      pourInCup();        
      addSugarAndMilk();    
  }      
  public void boilWater() {        
      System.out.println("将水煮沸");    
  }
  public void brewCoffeeGrings() {        
      System.out.println("冲泡咖啡");    
  }  
  public void pourInCup() {        
      System.out.println("把咖啡倒进杯子中");    
  }
  public void addSugarAndMilk() {        
      System.out.println("加糖和牛奶");    
  }
}

class Tea {    
  /*  
    * 冲泡茶法(算法)    
  */    
  void prepareRecipe() {        
      boilWater();        
      steepTeaBag();        
      pourInCup();        
      addLemon();    
  }      
  public void boilWater() {        
      System.out.println("将水煮沸");    
  }
  public void steepTeaBag() {        
      System.out.println("浸泡茶叶");    
  }  
  public void pourInCup() {        
      System.out.println("把茶倒进杯子中");    
  }
  public void addLemon() {        
      System.out.println("加柠檬");    
  }
}
class Test {
  public static void main(String[] agrs) {
      Coffee coffee = new Coffee();
      Tea tea = new Tea();
      coffee.prepareRecipe();
      tea.prepareRecipe();
  }
}

我们在这两个类中发现了重复代码,因此我们需要重新理一下我们的设计。

  • 既然茶和咖啡是如此的相似,因此我们应该将共同的部分抽取出来,放进一个基类中。
  • 从冲泡法入手。观察咖啡和茶的冲泡法我们会发现,两种冲泡法都采用了相同的算法: 将水煮沸 用热水泡饮料 把饮料倒进杯子 在饮料内加入适当的调料

实际上,浸泡(steep)和冲泡(brew)差异并不大。因此我们给它一个新的方法名称brew(),这样我们无论冲泡的是何种饮 料都可以使用这个方法。同样的,加糖、牛奶还是柠檬也很相似,都是在饮料中加入其它调料,因此我们也给它一 个通用名称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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/** 
* 咖啡因饮料是一个抽象类
**/
abstract class CaffeineBeverage {    
  /**
    * 现在用同一个prepareRecipe()方法处理茶和咖啡。    
    * 声明为final的原因是我们不希望子类覆盖这个方法!    
  **/    
  final void prepareRecipe() {        
      boilWater();        
      brew();        
      pourInCup();        
      addCondiments();  
  }
    /**    
    * 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现    
    **/    
  abstract void brew();    
  abstract void addCondiments();

  void boilWater() {        
        System.out.println("将水煮沸");    
  }
  void pourInCup() {        
        System.out.println("把饮料倒进杯子中");    
  }
}

class Coffee extends CaffeineBeverage {    
  public void brew() {        
      System.out.println("冲泡咖啡");    
  }  
  public void addCondiments() {        
      System.out.println("加糖和牛奶");    
  }
}

class Tea extends CaffeineBeverage {    
  public void brew() {        
      System.out.println("浸泡茶叶");    
  }  
  public void addCondiments() {        
      System.out.println("加柠檬");    
  }
}
class Test {
  public static void main(String[] agrs) {
      CaffeineBeverage coffee = new Coffee();
      CaffeineBeverage tea = new Tea();
      coffee.prepareRecipe();
      tea.prepareRecipe();
  }
}

img

模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供具体实现

在模板设计模式下还有一种钩子用法 钩子方法是一类"默认不做事的方法" ,子类可以视情况决定要不要覆盖它们。 比如说,顾客点杯咖啡时,可以选择加不加牛奶或者糖!!!

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import java.util.Scanner;

/**
* 咖啡因饮料是一个抽象类
**/
abstract class CaffeineBeverage {    
  /**
    * 现在用同一个prepareRecipe()方法处理茶和咖啡。    
    * 声明为final的原因是我们不希望子类覆盖这个方法!    
  **/    
  final void prepareRecipe() {        
      boilWater();        
      brew();        
      pourInCup();  
      if(customerWantsCondiments())    
          addCondiments();  
  }
    /**    
    * 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现    
    **/    
  abstract void brew();    
  abstract void addCondiments();

  void boilWater() {        
        System.out.println("将水煮沸");    
  }
  void pourInCup() {        
        System.out.println("把饮料倒进杯子中");    
  }
  boolean customerWantsCondiments() {
      return true;
  }
}

class Coffee extends CaffeineBeverage {    
  public void brew() {        
      System.out.println("冲泡咖啡");    
  }  
  public void addCondiments() {        
      System.out.println("加糖和牛奶");    
  }

    /**  
    * 子类覆写了钩子函数,实现自定义功能  
    **/
  boolean customerWantsCondiments() {
      String result = getUserInput();
      if(result.equals("y"))
          return true;
      else
          return false;
  }    
  private String getUserInput() {
      System.out.println("您想要在咖啡中加入牛奶或糖吗(y/n)?");
      Scanner scanner = new Scanner(System.in);
      String str = scanner.nextLine();
      return str;
  }
}

class Tea extends CaffeineBeverage {    
  public void brew() {        
      System.out.println("浸泡茶叶");    
  }  
  public void addCondiments() {        
      System.out.println("加柠檬");    
  }
}
class Test {
  public static void main(String[] agrs) {
      CaffeineBeverage coffee = new Coffee();
      CaffeineBeverage tea = new Tea();
      coffee.prepareRecipe();
      tea.prepareRecipe();
  }
}