框架建立中常見設計模式-模板方法模式
阿新 • • 發佈:2018-12-22
模板方法模式
定義:在一個方法中定義一個演算法的骨架,將一些處理的步驟延遲到子類去做處理,可以使在子類不改變演算法結構的情況下, 重新定義演算法的步驟。
設計院原則
好萊塢原則:別調用我們,我們會呼叫你。
先來看下簡單的程式碼實現:
定義一個父類:果汁流程製作
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.策略模式和模板方法模式都封裝演算法,一個用組合,一個用繼承