不知道為什么,這幾天對Java中的設計模式非常感興趣,恰巧呢這幾天公司的開發任務還不算太多,趁着有時間昨天又把模板方法模式深入學習了一下,做了一個客戶在不同銀行計息的小案例,感觸頗深,今天給各位分享一下,可能有些常識我在程序中運用的不是很到位,希望各位諒解。
模板方法模式呢,按我意思理解:就是將完成某件事情固定不變的步驟設計成模板類用final修飾的方法,然后將不確定的業務邏輯設計成抽象的方法,目的就是讓子類繼承並且復寫該抽象方法,能夠為了實現可擴展性。官方的說法:定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
模板方法模式是一種基於繼承的代碼復用技術,它是一種類行為型模式。
模板方法模式是結構最簡單的行為型設計模式,在其結構中只存在父類與子類之間的繼承關系。通過使用模板方法模式,可以將一些復雜流程的實現步驟封裝在一系列基本方法中,在抽象父類中提供一個稱之為模板方法的方法來定義這些基本方法的執行次序,而通過其子類來覆蓋某些步驟,從而使得相同的算法框架可以有不同的執行結果。模板方法模式提供了一個模板方法來定義算法框架,而某些具體步驟的實現可以在其子類中完成。
好了,讀到這兒你估計也懵懵逼逼的,我還是喜歡用事實說話,看碼。
【案例一】:就是同一個客戶在不同銀行分別儲蓄了一定金額的存款,2年之后他想知道在每個銀行分別能拿到多少本息?(本息=本金+利息)(利息=本金*利率*存期*100%)
設計思想:
通過分析得出,不管他想知道那個銀行的本息,都是通過本金+利息的方法得到的,但是問題來了,每個銀行計算利息的方式是不同的,當然不是說方式不同而是每個銀行根據客戶的存期參與計算的利率是不相同的。那么可以根據模板方法模式,可以將計算本息的過程設計成用final修飾的方法,而計算利息的過程可以設計成抽象的方法,然后可以由每個銀行類通過繼承模板類並復寫計算利息的方法來計算出每個銀行的利息,最后得出本息。
實現過程:
模板類,將計算本息的過程設計成模板方法,用關鍵詞final修飾(因為用final修飾的方法是不能被復寫的,final修飾的變量是不能被重新賦值的,final修飾的類是不能被繼承的),然后將計算利息的過程設計成抽象方法,任由子類復寫,最后在模板方法中會調用抽象方法,這也是模板設計模式的特性。
1 /** 2 * 模板類:每個銀行獲取本息的方式是固定不變的,但是利息是通過每個銀行自己的規定的利率計算得到的。 3 * 利息=本金*存期*利率 4 * 本息=本金+利息 5 */ 6 abstract class Interest{ 7 /** 8 * 獲取本息 9 * @param capital:本金 10 * @return 11 */ 12 public final Double getCapital(Double capital){ 13 return capital+getInterest(); 14 } 15 //獲取利息 16 public abstract Double getInterest(); 17 }
由建設銀行和浦發銀行分別繼承上面的模板類,然后通過各自的利率規則計算出客戶的利息,其實這里我的程序並不靈活,假設在實際開發場景中,客戶的本金和存期都是由數據庫查詢出來的。(走到這兒我還有一個小問題想請教各位,就是一直想把每個銀行計算利息的那段代碼再優化一下,switch語句那塊代碼把它也想放到final修飾的方法中,但是昨天下班腦子實在轉不動了,后期有機會再優化吧)
1 /** 2 * 建設銀行 3 */ 4 class JSBank extends Interest{ 5 //獲取指定用戶的本金、存期等數據 6 public double capital = 38654.6; 7 public int time = 24; 8 //計算客戶利息 9 @Override 10 public Double getInterest() { 11 int num = time/12; //根據用戶存期得到年利率 12 switch(num){ 13 case 1: 14 return capital*0.0175*num; //一年 15 case 2: 16 return capital*0.0225*num; //二年 17 case 3: 18 return capital*0.0275*num; //三年 19 case 5: 20 return capital*0.0275*num; //五年 21 } 22 return capital*0.0135; //未滿一年 23 } 24 } 25 26 /** 27 * 浦發銀行 28 */ 29 class PFBank extends Interest{ 30 //獲取指定用戶的本金、存期等數據 31 public double capital = 38654.6; 32 public int time = 24; 33 @Override 34 public Double getInterest() { 35 int num = time/12; //根據用戶存期得到年利率 36 switch(num){ 37 case 1: 38 return capital*0.02*num; //一年 39 case 2: 40 return capital*0.024*num; //二年 41 case 3: 42 return capital*0.028*num; //三年 43 case 5: 44 return capital*0.028*num; //五年 45 } 46 return capital*0.0135; //未滿一年 47 } 48 }
最后來測試一下,我這里的客戶是科比 布萊恩特(因為他一直是我的偶像,我喜歡他,喜歡他的性格,喜歡他的一切,他的故事一直在激勵着我,一直感謝我生命里有他,請允許我附一張他的圖片,哈哈哈~~~):
1 /** 2 * 模板設計模式 3 * 需求:科比分別在建設銀行和浦發銀行等存蓄一定金額,2年后他想知道他在每個銀行的本息是多少? 4 */ 5 6 public class TemplateMethodLiXi { 7 8 public static void main(String[] args) { 9 //建行 10 JSBank js = new JSBank(); 11 Double jsPrincipalAndInterest = js.getCapital(js.capital); 12 System.out.println("建設銀行(科比):"+jsPrincipalAndInterest); 13 //浦行 14 PFBank pf = new PFBank(); 15 Double pfPrincipalAndInterest = pf.getCapital(pf.capital); 16 System.out.println("浦發銀行(科比):"+pfPrincipalAndInterest); 17 } 18 19 }
【案例二】:計算一段代碼的運行時長?
這個案例我不多說設計思想和實現過程,因為實現理念是類似的,請各位賞臉讀讀鄙人寫的代碼,它主要就是計算子類繼承模板類,然后子類復寫抽象方法並實現自己的業務邏輯,最后還是通過模板方法調用抽象方法來靈活達到計算不固定代碼塊的運行時間。
1 /** 2 * 模板方法模式 3 * 需求:計算自定義類中某個方法(可變的)的運行時長? 4 */ 5 6 public class TemplateMethod { 7 public static void main(String[] args) { 8 MyCode my = new MyCode(); 9 Long runtime = my.getTime(); //調用父類的方法獲取子類方法的運行時長 10 System.out.println("運行時間:"+runtime); 11 } 12 } 13 14 /** 15 * 模板類:計算一段未知代碼的運行時長。 16 */ 17 abstract class GetCodesRunTime{ 18 /** 19 * 專門獲取一段代碼的運行時間 20 * @return 21 */ 22 public final Long getTime(){ 23 long startTime = System.currentTimeMillis(); //開始時間 24 runCode(); //調用本類抽象方法 25 long endTime = System.currentTimeMillis(); //終止時間 26 27 return endTime-startTime; 28 } 29 //由子類復寫,然后計算子類方法中代碼的運行時長 30 public abstract void runCode(); 31 } 32 33 /** 34 *自定義代碼塊,繼承模板類,然后復寫父類的擴展方法 35 */ 36 class MyCode extends GetCodesRunTime{ 37 @Override 38 public void runCode() { 39 for(int i=0; i<=20000; i++){ 40 System.out.println(i); 41 } 42 } 43 }
設計模式的學習還在繼續,后期還會給各位分享其他設計模式的學習心得和案例的實現過程。