1. 程式人生 > >框架建立中常見設計模式-模板方法模式

框架建立中常見設計模式-模板方法模式

模板方法模式
定義:在一個方法中定義一個演算法的骨架,將一些處理的步驟延遲到子類去做處理,可以使在子類不改變演算法結構的情況下, 重新定義演算法的步驟。

設計院原則

好萊塢原則:別調用我們,我們會呼叫你。

先來看下簡單的程式碼實現:

定義一個父類:果汁流程製作

 1 package com.templateModePattern.parentClass;
 2 
 3 /**
 4  * @program: test
 5  * @description: 果汁的製造簡單流程:--》清洗水果 --》放入修飾材料--》放入榨汁機中
 6  *               因為放入修飾材料的流程,各個果汁不一樣,所以放入子類去實現
7 * @author: Mr.Yang 8 * @create: 2018-12-22 13:19 9 **/ 10 public abstract class FruitJuiceParent { 11 12 /** 13 * 果汁的製造流程(當作固定流程,不會變),這個方法不希望子類去覆蓋, 14 *子類只需要實現putMaterial()方法就行,宣告為final,骨架方法 15 */ 16 public final void makeFruitJuice(){ 17 cleanIts(); 18 putMaterial();
19 putMachine(); 20 } 21 22 protected abstract void putMaterial(); 23 24 /** 25 * 清洗水果 26 */ 27 protected void cleanIts(){ 28 System.out.println("clean fruit"); 29 } 30 31 /** 32 * 放入榨汁機中 33 */ 34 protected void putMachine(){ 35 System.out.println("put machine");
36 } 37 }

 

 

子類蘋果汁,實現父類未實現的方法

 1 package com.templateModePattern.subClass;
 2 
 3 import com.templateModePattern.parentClass.FruitJuiceParent;
 4 
 5 /**
 6  * @program: test
 7  * @description: 蘋果汁
 8  * @author: Mr.Yang
 9  * @create: 2018-12-22 13:26
10  **/
11 public class AppleFruitJuice extends FruitJuiceParent {
12 
13     /**
14      * 在蘋果汁放入蜂蜜,味道更好
15      */
16     public void putMaterial() {
17         System.out.println("Put in honey");
18     }
19 
20 }

 

 

子類西瓜汁,實現父類未實現的方法

 1 package com.templateModePattern.subClass;
 2 
 3 import com.templateModePattern.parentClass.FruitJuiceParent;
 4 
 5 /**
 6  * @program: test
 7  * @description: 西瓜汁
 8  * @author: Mr.Yang
 9  * @create: 2018-12-22 13:29
10  **/
11 public class WatermelonFruitJuice extends FruitJuiceParent {
12 
13     /**
14      * 放入牛奶,味道更好
15      */
16     public void putMaterial() {
17         System.out.println("put in milk");
18     }
19 }

 

 

掛鉤

鉤子是一種被宣告在抽象類中的方法,但只有空的或者預設實現,鉤子的存在,可以讓子類有能力對演算法的不同點進行掛鉤,要不要掛鉤,由子類自行決定。

在方法中加入掛鉤程式碼實現

父類加入判斷,如果true,去執行,讓子類去具體實現該方法,做處理

 1 package com.templateModePattern.parentClass;
 2 
 3 /**
 4  * @program: test
 5  * @description: 果汁的製造簡單流程:--》清洗水果 --》放入修飾材料--》放入榨汁機中
 6  *               因為放入修飾材料的流程,各個果汁不一樣,所以放入子類去實現
 7  * @author: Mr.Yang
 8  * @create: 2018-12-22 13:19
 9  **/
10 public  abstract  class FruitJuiceParent {
11 
12     /**
13      * 果汁的製造流程(當作固定流程,不會變),這個方法不希望子類去覆蓋,
14      * 子類只需要實現putMaterial()方法就行,宣告為final
15      */
16     public final void makeFruitJuice(){
17         cleanIts();
18         if(isPutMaterIal()){
19             putMaterial();
20         }
21         putMachine();
22     }
23 
24     protected abstract void putMaterial();
25 
26     /**
27      * 清洗水果
28      */
29     protected void cleanIts(){
30         System.out.println("clean fruit");
31     }
32 
33     /**
34      * 放入榨汁機中
35      */
36     protected void putMachine(){
37         System.out.println("put machine");
38     }
39 
40     /**
41      * 父類增加判斷
42      * @return
43      */
44     protected boolean isPutMaterIal(){
45         return true;
46     }
47 }

 

 

蘋果汁子類讓使用者去做選擇

 1 package com.templateModePattern.subClass;
 2 
 3 import com.templateModePattern.parentClass.FruitJuiceParent;
 4 import org.apache.commons.lang3.StringUtils;
 5 
 6 import java.io.BufferedReader;
 7 import java.io.IOException;
 8 import java.io.InputStreamReader;
 9 
10 /**
11  * @program: test
12  * @description: 蘋果汁
13  * @author: Mr.Yang
14  * @create: 2018-12-22 13:26
15  **/
16 public class AppleFruitJuice extends FruitJuiceParent {
17 
18     /**
19      * 在蘋果汁放入蜂蜜,味道更好
20      */
21     public void putMaterial() {
22         System.out.println("Put in honey");
23     }
24 
25     /**
26      * 在子類覆蓋它,讓使用者去選擇
27      * @return
28      */
29     @Override
30     protected boolean isPutMaterIal() {
31         String userInput = getUserInput();
32         if(userInput.toLowerCase().startsWith("y")){
33             return true;
34         }else{
35             return false;
36         }
37     }
38 
39     /**
40      * 得到使用者輸入的內容
41      * @return
42      */
43     private String getUserInput(){
44         String readString=null;
45         System.out.println("Do you want honey(y/n)?");
46         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
47         try {
48             readString = br.readLine();
49         } catch (IOException e) {
50             System.out.println("---異常---"+e);
51         }
52         if(StringUtils.isEmpty(readString)){
53             return "N";
54         }else{
55             return readString;
56         }
57     }
58 }

 

 

西瓜汁子類讓使用者去做選擇

 1 package com.templateModePattern.subClass;
 2 
 3 import com.templateModePattern.parentClass.FruitJuiceParent;
 4 import org.apache.commons.lang3.StringUtils;
 5 
 6 import java.io.BufferedReader;
 7 import java.io.IOException;
 8 import java.io.InputStreamReader;
 9 
10 /**
11  * @program: test
12  * @description: 西瓜汁
13  * @author: Mr.Yang
14  * @create: 2018-12-22 13:29
15  **/
16 public class WatermelonFruitJuice extends FruitJuiceParent {
17 
18     /**
19      * 放入牛奶,味道更好
20      */
21     public void putMaterial() {
22         System.out.println("put in milk");
23     }
24 
25 
26     /**
27      * 在子類覆蓋它,讓使用者去選擇
28      * @return
29      */
30     @Override
31     protected boolean isPutMaterIal() {
32         String userInput = getUserInput();
33         if(userInput.toLowerCase().startsWith("y")){
34             return true;
35         }else{
36             return false;
37         }
38     }
39 
40     /**
41      * 得到使用者輸入的內容
42      * @return
43      */
44     private String getUserInput(){
45         String readString=null;
46         System.out.println("Do you want milk(y/n)?");
47         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
48         try {
49             readString = br.readLine();
50         } catch (IOException e) {
51             System.out.println("---異常---"+e);
52         }
53         if(StringUtils.isEmpty(readString)){
54             return "N";
55         }else{
56             return readString;
57         }
58     }
59 }

 

 

製造無新增草莓汁

 1 package com.templateModePattern.subClass;
 2 
 3 import com.templateModePattern.parentClass.FruitJuiceParent;
 4 
 5 /**
 6  * @program: test
 7  * @description: 草莓汁,無新增
 8  * @author: Mr.Yang
 9  * @create: 2018-12-22 13:46
10  **/
11 public class StrawberryFruitJuice extends FruitJuiceParent {
12 
13     protected void putMaterial() {
14 
15     }
16 
17     @Override
18     protected boolean isPutMaterIal() {
19         return false;
20     }
21 }

 

 

測試結果:

 1 ____________蘋果汁製作開始____________
 2 clean fruit
 3 Do you want honey(y/n)?
 4 y
 5 Put in honey
 6 put machine
 7 ____________蘋果汁製作結束____________
 8 
 9 
10 ____________草莓汁製作開始____________
11 clean fruit
12 put machine
13 ____________草莓汁製作結束____________
14 
15 
16 ____________西瓜汁製作____________
17 clean fruit
18 Do you want milk(y/n)?
19 n
20 put machine
21 ____________西瓜汁結束____________
 

好萊塢原則與依賴倒置原則的區別

依賴倒置提倡避免使用具體類,多使用抽象。
好萊塢原則是用在建立框架或元件上的一種技巧,讓底層元件能夠被掛鉤計算中,又不會讓高層元件依賴低層元件。

重點內容與比較

1.模板方法定義了演算法的步驟,將步驟的例項延遲到子類
2.提供了一種程式碼複用的技巧
3.鉤子的瞭解與使用
4.好萊塢原則提倡將決策權放到高層(父類)
5.策略模式和模板方法模式都封裝演算法,一個用組合,一個用繼承