模板方法及策略设计模式实践
一、前言
? ? ? ?最近兩周工作比較忙,一直疏忽了寫(xiě)博客這件事。但是再忙也得堅(jiān)持下去,雖然很難,但是自己定下的小目標(biāo)含著淚也要把它做下去啊~~好了,廢話(huà)不多說(shuō)直接進(jìn)入正題吧。
? ? ? ?設(shè)計(jì)模式相信大家應(yīng)該都有接觸過(guò),并且在不經(jīng)意間也使用過(guò)一些設(shè)計(jì)模式,使用設(shè)計(jì)模式最重要的一點(diǎn)是讓我們的代碼看起來(lái)更清晰可讀,并且更具可擴(kuò)展性,還可以達(dá)到代碼最大程度復(fù)用的目標(biāo)。所以今天我就來(lái)介紹一下最近在項(xiàng)目中用到的兩個(gè)設(shè)計(jì)模式,這兩個(gè)設(shè)計(jì)模同樣也是使用的比較多的兩個(gè)模式:模板方法模式以及策略模式。
二、模板方法模式
? ? ? ?模板方法模式是類(lèi)的行為模式。準(zhǔn)備一個(gè)抽象類(lèi),將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類(lèi)實(shí)現(xiàn)剩余的邏輯。不同的子類(lèi)可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。這就是模板方法模式的用意。
? ? ? ?模板方法模式是所有模式中最為常見(jiàn)的幾個(gè)模式之一,是基于繼承的代碼復(fù)用的基本技術(shù)。模板方法模式需要開(kāi)發(fā)抽象類(lèi)和具體子類(lèi)。抽象類(lèi)給出一個(gè)算法的輪廓和骨架,具體類(lèi)則負(fù)責(zé)給出這個(gè)算法的各個(gè)各個(gè)特定邏輯步驟。代表這些具體邏輯步驟的方法稱(chēng)做基本方法(primitive method);而將這些基本方法匯總起來(lái)的方法叫做模板方法(template method),這個(gè)設(shè)計(jì)模式的名字就是從此而來(lái)。
? ? ? 模板方法所代表的行為稱(chēng)為頂級(jí)行為,其邏輯稱(chēng)為頂級(jí)邏輯。模板方法模式的靜態(tài)結(jié)構(gòu)圖如下所示:
這里涉及到兩個(gè)角色:
抽象模板(Abstract Template)角色有如下責(zé)任:
■ 定義了一個(gè)或多個(gè)抽象操作,以便讓子類(lèi)實(shí)現(xiàn)。這些抽象操作叫做基本操作,它們是一個(gè)頂級(jí)邏輯的組成步驟。
■ 定義并實(shí)現(xiàn)了一個(gè)模板方法。這個(gè)模板方法一般是一個(gè)具體方法,它給出了一個(gè)頂級(jí)邏輯的骨架,而邏輯的組成步驟在相應(yīng)的抽象操作中,推遲到子類(lèi)實(shí)現(xiàn)。頂級(jí)邏輯也有可能調(diào)用一些具體方法。
具體模板(Concrete Template)角色又如下責(zé)任:
■ 實(shí)現(xiàn)父類(lèi)所定義的一個(gè)或多個(gè)抽象方法,它們是一個(gè)頂級(jí)邏輯的組成步驟。
■ 每一個(gè)抽象模板角色都可以有任意多個(gè)具體模板角色與之對(duì)應(yīng),而每一個(gè)具體模板角色都可以給出這些抽象方法(也就是頂級(jí)邏輯的組成步驟)的不同實(shí)現(xiàn),從而使得頂級(jí)邏輯的實(shí)現(xiàn)各不相同。
? ? ? 有句名言說(shuō)的好:Talk is cheap. Show me the code…拿出代碼才有說(shuō)服力嘛!那么我們就以炒菜這件事為例來(lái)實(shí)現(xiàn)一下模板方法模式吧。首先我們來(lái)梳理一下,如果要做一個(gè)菜需要做些什么事項(xiàng),以及這些事項(xiàng)的順序。俗話(huà)說(shuō),巧婦難為無(wú)米之炊。。。那最先肯定需要原材料,所以就需要去買(mǎi)菜;買(mǎi)到菜之后就是考驗(yàn)動(dòng)手能力的時(shí)候了,需要洗菜,切菜以及做菜了。通過(guò)我個(gè)人總結(jié),這個(gè)世界上的所有菜都有個(gè)基本操作順序:買(mǎi)菜-->洗菜-->準(zhǔn)備菜-->做菜-->出鍋,所有菜的做法都是大概就是這個(gè)抽象的流程。因此,我們可以構(gòu)建一個(gè)抽象類(lèi)來(lái)定義這些流程,代碼如下:
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 炒菜抽象類(lèi)(定義炒菜的具體步驟) 5 */ 6 public abstract class AbstractCook { 7 8 protected String dishName = ""; 9 10 public void fryProcess() { 11 // 設(shè)置菜名 12 this.setDishName(); 13 // 步驟一: 買(mǎi)菜 14 this.buyDishes(); 15 // 步驟二: 洗菜 16 this.washDishes(); 17 // 步驟三: 準(zhǔn)備菜 18 this.prepareDisher(); 19 // 步驟四: 做菜 20 this.fryDishes(); 21 // 步驟五: 出鍋 22 this.finishDishes(); 23 } 24 25 // 設(shè)置菜名 26 protected abstract void setDishName(); 27 28 // 買(mǎi)菜通用步驟 29 protected void buyDishes() { 30 System.out.println("去菜市場(chǎng)買(mǎi):" + dishName); 31 } 32 33 // 洗菜通用步驟 34 protected void washDishes() { 35 System.out.println("將" + dishName + "用水洗干凈"); 36 } 37 38 // 準(zhǔn)備菜(各類(lèi)菜準(zhǔn)備方式不一樣,此處使用抽象方法) 39 protected abstract void prepareDisher(); 40 41 // 做菜(各類(lèi)菜做法不一樣,此處抽象) 42 protected abstract void fryDishes(); 43 44 // 出鍋 45 protected void finishDishes() { 46 System.out.println("將" + dishName + "盛出并裝盤(pán)"); 47 } 48 49 }? ? ? ?在這里我們規(guī)定了做菜的通用流程,像買(mǎi)菜、洗菜以及出鍋這些步驟,所有菜的做法基本都是一樣的,所以可以將其方法的實(shí)現(xiàn)定義在抽象類(lèi)中,如有特別需要也可對(duì)相應(yīng)步驟進(jìn)行重寫(xiě);而準(zhǔn)備菜和做菜這兩個(gè)步驟,不同類(lèi)型的才的做法差異較大,所以需要定義為抽象方法,讓具體的菜做法實(shí)現(xiàn)類(lèi)去實(shí)現(xiàn)這兩個(gè)步驟。接下來(lái)就是定義具體的菜的做法類(lèi),我這邊給出了三個(gè)具有代表性的菜類(lèi)的做法,分別是:炒青菜、蒸雞蛋以及燒排骨,下面具體來(lái)看一下他們的實(shí)現(xiàn):
1、? 炒青菜
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 炒青菜類(lèi) 5 */ 6 public class CookGreens extends AbstractCook { 7 @Override 8 protected void setDishName() { 9 this.dishName = "青菜"; 10 } 11 12 @Override 13 protected void prepareDisher() { 14 System.out.println("開(kāi)始摘" + dishName + "..."); 15 System.out.println("開(kāi)始切" + dishName + "..."); 16 System.out.println("開(kāi)始對(duì)" + dishName + "進(jìn)行過(guò)水..."); 17 System.out.println("開(kāi)始準(zhǔn)備蒜瓣, 并將其切碎..."); 18 } 19 20 @Override 21 protected void fryDishes() { 22 System.out.println("倒油并燒開(kāi)..."); 23 System.out.println("倒入蒜瓣爆香..."); 24 System.out.println("倒入" + dishName + "進(jìn)行翻炒3分鐘..."); 25 } 26 }? ? ?實(shí)現(xiàn)了準(zhǔn)備菜和做菜這兩個(gè)抽象方法,內(nèi)容對(duì)應(yīng)青菜的做法。
2、? 蒸雞蛋
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 蒸雞蛋類(lèi) 5 */ 6 public class CookEggs extends AbstractCook { 7 @Override 8 protected void setDishName() { 9 this.dishName = "雞蛋"; 10 } 11 12 @Override 13 protected void washDishes() { 14 // 雞蛋不需要清洗,這里用一個(gè)空方法重寫(xiě)覆蓋 15 } 16 17 @Override 18 protected void prepareDisher() { 19 System.out.println("打破" + dishName + "殼,并放入碗中..."); 20 System.out.println("搗碎" + dishName + "蛋黃和蛋清, 攪拌至均勻..."); 21 System.out.println("倒入一定量的清水以及油和鹽..."); 22 } 23 24 @Override 25 protected void fryDishes() { 26 System.out.println("在蒸鍋中放入一定量水..."); 27 System.out.println("放入隔層..."); 28 System.out.println("放入準(zhǔn)備好的" + dishName + ", 蓋上鍋蓋蒸10到15分鐘..."); 29 } 30 }? ? ? ?實(shí)現(xiàn)蒸雞蛋的準(zhǔn)備方法與做法,另外雞蛋不需要洗,所以把洗菜這個(gè)方法實(shí)現(xiàn)成了一個(gè)空的方法,表示這個(gè)菜是不需要清洗的。
3、? 燒排骨
1 package com.guigui.common.patterns.template; 2 3 /** 4 * 燒排骨類(lèi) 5 */ 6 public class CookRibs extends AbstractCook{ 7 @Override 8 protected void setDishName() { 9 this.dishName = "排骨"; 10 } 11 12 @Override 13 protected void prepareDisher() { 14 System.out.println("剁碎洗干凈后的" + dishName + "..."); 15 System.out.println("準(zhǔn)備一定量的蔥姜蒜,并切碎..."); 16 System.out.println("將準(zhǔn)備好的" + dishName + "在鍋中進(jìn)行焯水處理后盛出..."); 17 } 18 19 @Override 20 protected void fryDishes() { 21 System.out.println("在鍋中倒入油并燒開(kāi)..."); 22 System.out.println("倒入準(zhǔn)備好的蔥姜蒜爆香..."); 23 System.out.println("放入焯水后的排骨進(jìn)行翻炒5到10分鐘..."); 24 System.out.println("倒入一定量料酒,及其他調(diào)料,繼續(xù)翻炒幾分鐘..."); 25 } 26 }? ? ? 實(shí)現(xiàn)了排骨的具體做法。
? ? ? 以上便是模板方法模式的具體實(shí)現(xiàn);下面繼續(xù)介紹另外一個(gè)常用的設(shè)計(jì)模式:策略模式,策略模式經(jīng)常和模板方法模式結(jié)合起來(lái)使用,模板方法規(guī)定主流程,具體的實(shí)現(xiàn)分別對(duì)應(yīng)不同的策略。
三、策略模式
? ? ? ?策略模式屬于對(duì)象的行為模式。其用意是針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類(lèi)中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶(hù)端的情況下發(fā)生變化。
? ? ? 策略模式是對(duì)算法的包裝,是把使用算法的責(zé)任和算法本身分割開(kāi)來(lái),委派給不同的對(duì)象管理。策略模式通常把一個(gè)系列的算法包裝到一系列的策略類(lèi)里面,作為一個(gè)抽象策略類(lèi)的子類(lèi)。用一句話(huà)來(lái)說(shuō),就是:“準(zhǔn)備一組算法,并將每一個(gè)算法封裝起來(lái),使得它們可以互換”。下圖為策略模式具體結(jié)構(gòu):
?
? ? ?這個(gè)模式涉及到三個(gè)角色:
● 環(huán)境(Context)角色:持有一個(gè)Strategy的引用。
● 抽象策略(Strategy)角色:這是一個(gè)抽象角色,通常由一個(gè)接口或抽象類(lèi)實(shí)現(xiàn)。此角色給出所有的具體策略類(lèi)所需的接口。
● 具體策略(ConcreteStrategy)角色:包裝了相關(guān)的算法或行為。
? ? ? 還是以上述做菜的例子為例說(shuō)明,做菜的抽象流程類(lèi)便是抽象策略角色,每一種菜的實(shí)現(xiàn)都可以當(dāng)做是一個(gè)具體策略角色,然后我們還需要加上一個(gè)環(huán)境角色,來(lái)完善這個(gè)策略模式,下面代碼示例即為環(huán)境角色類(lèi):
1 package com.guigui.common.patterns.strategy; 2 3 import com.guigui.common.patterns.template.AbstractCook; 4 5 public class Context { 6 // 持有具體策略對(duì)象 7 private AbstractCook cookDish; 8 // 構(gòu)造方法,傳入具體策略對(duì)象 9 public Context(AbstractCook cookDish) { 10 this.cookDish = cookDish; 11 } 12 // 執(zhí)行策略方法 13 public void strategyProcess(){ 14 cookDish.fryProcess(); 15 } 16 }? ? ? 這樣便完成了策略模式的基本結(jié)構(gòu)。下面寫(xiě)一個(gè)示例來(lái)演示一下模板方法模式和策略模式結(jié)合起來(lái)使用過(guò)程,以下為測(cè)試類(lèi)代碼:
1 package com.guigui.common.patterns; 2 3 import com.guigui.common.patterns.template.AbstractCook; 4 import com.guigui.common.patterns.template.CookEggs; 5 import com.guigui.common.patterns.template.CookGreens; 6 import com.guigui.common.patterns.template.CookRibs; 7 import com.guigui.common.patterns.strategy.Context; 8 9 import java.util.HashMap; 10 import java.util.Map; 11 12 public class StrategyMain { 13 private static Map<String, AbstractCook> strategyMap = new HashMap<>(); 14 static { 15 strategyMap.put("greens", new CookGreens()); 16 strategyMap.put("eggs", new CookEggs()); 17 strategyMap.put("ribs", new CookRibs()); 18 } 19 public static void main(String[] args) { 20 // 炒青菜 21 Context green_context = new Context(strategyMap.get("greens")); 22 green_context.strategyProcess(); 23 System.out.println("=========================================="); 24 25 // 蒸雞蛋 26 Context egg_context = new Context(strategyMap.get("eggs")); 27 egg_context.strategyProcess(); 28 System.out.println("=========================================="); 29 30 // 燒排骨 31 Context rib_context = new Context(strategyMap.get("ribs")); 32 rib_context.strategyProcess(); 33 System.out.println("=========================================="); 34 } 35 }? ? ? 這個(gè)示例通過(guò)使用策略模式來(lái)做了三個(gè)菜,并且這三個(gè)菜的做法是通過(guò)一個(gè)模板方法來(lái)限定基本流程,而不同的做菜類(lèi)則是不同的做菜策略,只需要將具體的策略實(shí)例傳入環(huán)境角色,便可以實(shí)現(xiàn)對(duì)應(yīng)的做菜流程。
? ? ?測(cè)試結(jié)果:
? ? ?好了,今天要介紹的內(nèi)容大概就是這么多,有什么不對(duì)的地方歡迎大家指正,非常感謝~~~
?
轉(zhuǎn)載于:https://www.cnblogs.com/guishenyouhuo/p/10072897.html
總結(jié)
以上是生活随笔為你收集整理的模板方法及策略设计模式实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《Linux 性能及调优指南》1.6 了
- 下一篇: python:装饰器