一、引言 生活中有很多模板,如:簡歷模板、論文模板,PPT模板,所謂模板就是有一個特定的格式,但是可以根據自身的需求進行改動,然後實現自己的功能。這樣的好處就是可以減少自身的工作量,想想網上那麼多好的PPT模板,改吧改吧就成自己的,這是一件多酸爽的事情! 二、例子入手 現在有兩個類,分別是泡茶和泡咖 ...
一、引言
生活中有很多模板,如:簡歷模板、論文模板,PPT模板,所謂模板就是有一個特定的格式,但是可以根據自身的需求進行改動,然後實現自己的功能。這樣的好處就是可以減少自身的工作量,想想網上那麼多好的PPT模板,改吧改吧就成自己的,這是一件多酸爽的事情!
二、例子入手
現在有兩個類,分別是泡茶和泡咖啡
//泡咖啡類 public class Coffee { public void prepareRecipe(){ boilWater(); brewCoffeeGrinds(); pourInCup(); addSugarAndMilk(); } //燒沸水 public void boilWater(){ System.out.println("燒沸水"); } //沖泡咖啡 public void brewCoffeeGrinds(){ System.out.println("沖泡咖啡"); } //倒入杯中 public void pourInCup(){ System.out.println("倒入杯中"); } //加入糖和牛奶 public void addSugarAndMilk(){ System.out.println("加入糖和牛奶"); } } //泡茶類 public class Tea { public 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("加入檸檬"); } }
首先抽取相同的方法:boilWater()和pourInCup(),抽取完相同方法後,我們發現沖泡茶葉和沖泡咖啡動作是差不多的,那我們泛化成沖泡方法brew(),加入糖和牛奶和加入檸檬動作泛化成addCondiments(),這兩個方法子類實現不一,所以要寫成抽象方法,基類也就出來了。
實現如下:
//抽象基類 public abstract class CaffeineBeverage { //我們不希望子類覆蓋這個方法,用final關鍵字修飾 final public void prepareRecipe(){ boilWater(); brew(); pourInCup(); addCondiments(); } //沖泡方法 abstract void brew(); //加入作料 abstract void addCondiments(); //燒沸水 public void boilWater(){ System.out.println("燒沸水"); } //倒入杯中 public void pourInCup(){ System.out.println("倒入杯中"); } }
再來改造子類:由於父類已經把相同的方法實現,子類只要關註自身不同的方法,代碼就非常簡單了。
//泡咖啡類 public class Coffee extends CaffeineBeverage { //沖泡咖啡 public void brew(){ System.out.println("沖泡咖啡"); } //加入糖和牛奶 public void addCondiments(){ System.out.println("加入糖和牛奶"); } } //泡茶類 public class Tea extends CaffeineBeverage { //沖泡茶葉 public void brew(){ System.out.println("沖泡茶葉"); } //加入檸檬 public void addCondiments(){ System.out.println("加入檸檬"); } }
來,犒勞下自己
private static void template() { Tea tea=new Tea(); tea.prepareRecipe(); System.out.println("-----------"); Coffee coffee=new Coffee(); coffee.prepareRecipe(); }
輸出結果:
定義:在一個抽象類中定義一個操作中的演算法骨架(對應於生活中的大家下載的模板),而將一些步驟延遲到子類中去實現(對應於我們根據自己的情況向模板填充內容)。模板方法使得子類可以不改變一個演算法的結構前提下,重新定義演算法的某些特定步驟
意圖:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟。
主要解決:一些方法通用,卻在每一個子類都重新寫了這一方法。
何時使用:有一些通用的方法。
比如:有些人喜歡不加料的咖啡,如果按照之前的基類,那麼這個是無法實現的,我們試著用鉤子來實現不加料的咖啡。
先改動抽象基類:
//帶鉤子的模板方法基類 public abstract class CaffeineBerverageWithHook { //我們不希望子類覆蓋這個方法,用final關鍵字修飾 final public void prepareRecipe(){ boilWater() ; brew(); pourInCup(); if(customerWantCondiments()) { addCondiments(); } } //沖泡方法 abstract void brew(); //加入作料 abstract void addCondiments(); //燒沸水 public void boilWater(){ System.out.println("燒沸水"); } //倒入杯中 public void pourInCup(){ System.out.println("倒入杯中"); } //鉤子 boolean customerWantCondiments(){ return true; } }
再改造子類:
//實現鉤子的子類 public class CoffeeWithHook extends CaffeineBerverageWithHook { public void brew() { System.out.println("沖泡咖啡"); } public void addCondiments() { System.out.println("加入糖和牛奶"); } //重寫父類類方法 public boolean customerWantCondiments(){ String answer=getInput(); if(answer.toLowerCase().startsWith("y")){ return true; }else{ return false; } } private String getInput(){ String answer=null; System.out.println("您想要配料麽?"); BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); try{ answer=in.readLine(); }catch (IOException ex){} if(answer==null){ return "no"; }else{ return answer; } } }
看看
private static void templateHook() { CoffeeWithHook coffeeWithHook=new CoffeeWithHook(); coffeeWithHook.prepareRecipe(); }
結果:
三、總結
優點: 1、封裝不變部分,擴展可變部分。 2、提取公共代碼,便於維護。 3、行為由父類控制,子類實現。
缺點:每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。
使用場景: 1、有多個子類共有的方法,且邏輯相同。 2、重要的、複雜的方法,可以考慮作為模板方法。
註意事項:為防止惡意操作,一般模板方法都加上 final 關鍵詞。
源碼地址:https://gitee.com/yuanqinnan/pattern