Template Pattern 樣板模式 [Design Pattern in Java]

Posted by Kubeguts on 2020-07-29

樣板, 顧名思義就是把同樣的邏輯定義成一個樣板, 可讓其他類別共同使用

樣板也會開放可客製化的方法 (抽象方法), 供其他類別實作

樣板主要核心

  1. 將共同演算法邏輯包裝好
  2. 定義非共同的方法為抽像方法, 請使用樣板方法的類別自行實作

透過泡茶與泡咖啡來瞭解樣板模式吧

這時候若直接寫code, 會分別定義出 TeaCoffee class

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
class Tea {
Tea() {}

public void prepare() {
System.out.println("=======開始準備茶的動作=======");
boilWater();
steepTeaBag();
addLenmon();
pourIntoCup();
System.out.println("=======結束=======");
}

private void boilWater() {
System.out.println("燒開水");
}

private void steepTeaBag() {
System.out.println("泡開茶包");
}

private void addLenmon() {
System.out.println("加入檸檬");
}

private void pourIntoCup() {
System.out.println("倒入杯中");
}
}

class Coffee {
Coffee() {}

public void prepare() {
System.out.println("=======開始準備咖啡的動作=======");
boilWater();
greedCoffeeGrinds();
addCreamer();
pourIntoCup();
System.out.println("=======結束=======");
}

private void boilWater() {
System.out.println("燒開水");
}

private void greedCoffeeGrinds() {
System.out.println("磨咖啡豆");
}

private void addCreamer() {
System.out.println("加奶精");
}

private void pourIntoCup() {
System.out.println("倒入杯中");
}
}

使用以上定義的方法

1
2
3
4
5
6
7
8
9
public class main {
public static void main(String[] args) {
Tea tea = new Tea();
tea.prepare();

Coffee coffee = new Coffee();
coffee.prepare();
}
}

套用Template Pattern吧

仔細比較泡茶和泡咖啡, 都會有著以下類似的動作

  1. 煮沸開水
  2. 將(茶的茶包/咖啡藥磨成粉)泡開 <-- 這個動作一樣, 但茶是用茶包, 咖啡是需要先磨成粉
  3. 添加有的沒的 (咖啡會用奶精/茶會放檸檬) <-- 這邊的也是一樣
  4. 倒進杯中

剛好茶與咖啡都是含有咖啡因的飲料,這時就可以定義一個樣板叫做 CaffeineBeverage, 並把上述 2,3步驟,定義成抽象方法, 讓茶與咖啡類別自己去實作自己的邏輯

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
abstract class CaffeineBeverage {
String name = "咖啡因飲料";
CaffeineBeverage(String name) {
this.name = name;
}

public void prepare() {
System.out.println("=======開始準備 " + this.name + " 的動作=======");
boilWater();
brew();
addCondiments();
pourIntoCup();
System.out.println("=======結束=======");
}

private void boilWater() {
System.out.println("燒開水");
}

// 定義如何泡的抽象方法, 讓茶與咖啡自行定義
abstract void brew();
// 定義要添加其他配料, 讓茶與咖啡自行定義
abstract void addCondiments();

private void pourIntoCup() {
System.out.println("倒進杯中");
}
}

接著TeaCoffee實現該樣板類別 CaffeineBeverage
並根據自己的邏輯實作abstract方法 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
class Tea extends CaffeineBeverage {
Tea(String name) {
super(name);
}

public void prepare() {
System.out.println("=======開始準備茶的動作=======");
boilWater();
brew();
addCondiments();
pourIntoCup();
System.out.println("=======結束=======");
}

@Override
void brew() {
System.out.println("泡開茶包");
}

@Override
void addCondiments() {
System.out.println("加入檸檬");
}
}

class Coffee extends CaffeineBeverage{
Coffee(String name) {
super(name);
}

public void prepare() {
System.out.println("=======開始準備咖啡的動作=======");
boilWater();
brew();
addCondiments();
pourIntoCup();
System.out.println("=======結束=======");
}

@Override
void brew() {
System.out.println("磨咖啡豆");
}

@Override
void addCondiments() {
System.out.println("加奶精");
}
}

接著一樣使用之

1
2
3
4
5
6
7
8
9
public class main {
public static void main(String[] args) {
CaffeineBeverage tea = new Tea("茶");
tea.prepare();

CaffeineBeverage coffee = new Coffee("咖啡");
coffee.prepare();
}
}

小結

透過以上方法可以看到 樣板模式 將重複的地方給統整起來成為演算法邏輯, 並定義一個抽象類別, 將需要客製化的部分定義成抽象方法, 統一的部分就直接定義實體方法

如此一來各個類別在實作時,就不用在自己去做重複一樣的動作(例如燒開水, 倒入杯中) 只要直接繼承樣板抽象類別就有囉, 然後在自己實作自己需要的部分 (如茶有自己的茶包泡法, 咖啡則有自己的咖啡豆要磨 才能泡)

以實現樣板模式的精神

透過hook方法將封裝的演算法邏輯部分做控制使用

待補充