策略模式

1得哆、從項目"模擬鴨子游戲"實例講解策略模式:假如一個有個鴨子,他會叫哟旗、會飛贩据、膚色表現(xiàn)不一樣,有不同顏色的鴨子,而且叫聲不同、飛的方式也不同吧凉,要設計這樣一個項目

2、從面向對象的角度設計這個項目

2.1 以封裝近上、繼承、多態(tài)的三個特點會抽象出鴨子超類拂铡,擴展超類:

public abstract class Duck{

????public abstract void display();

? ? public void swim(){

????????System.out.println("I can swim");

? ? }

????public void quack(){

????????System.out.println("gaga~~");

? ? }

????public void fly(){

????????System.out.println("I can fly");

? ? }

}

2.2 然后不同定義不同顏色的鴨子繼承分類Duck:

public class GreenHeadDuckextends Duck {

@Override

? ? public void display() {

System.out.println("**GreenHead**");

? ? }

}

public class RedHeadDuckextends Duck {

@Override

? ? public void display() {

System.out.println("**ReadHeadDuck");

? ? }

}

2.3 若此時新增需求壹无,添加鴨子會飛的功能,當然按照面向對象的繼承原則感帅,我們可以在超類里添加一個fly()方法斗锭,

public abstract class Duck {

...;

public void Fly() {

System.out.println("~~im fly~~");

}

};

出現(xiàn)的問題:這個fly方法一加失球,讓所有的子類都會飛了岖是,這不科學。

繼承的問題:對類的局部改動实苞,尤其對超類的局部改動豺撑,會影響子類其他部分。影響會有溢出效應黔牵。

2.4 這時可以繼續(xù)按照面向對象的繼承以及多態(tài)原則進行改造前硫,把超類的fly()方法定義成抽象類,子類重寫這個fly方法

父類:

public abstract class Duck {

????...;

????public abstract void Fly() 荧止;

};

子類:

public class GreenHeadDuck extends Duck {

????...阶剑;

????public void Fly() {

????????System.out.println("~~no fly~~");

????}

}

2.5 但問題又來了跃巡,如果又新增一個需求,添加會游泳的驗證牧愁,而且不同的鴨子游泳的方式不一樣素邪,這時按照面向對象的原則,又在超類上添加一個抽象方法猪半,接著子類重寫兔朦。若再添加一個新功能偷线,還這樣無限重復。超類每挖一個坑沽甥,每個子類都要填声邦,增加工作量。不是好的設計方式摆舟。

那應該如何設計呢亥曹?

方法:分析項目中變化與不變的部分,提取變化部分恨诱,抽象成接口+實現(xiàn)

3媳瞪、利用策略模式改造項目

????3.1提取項目中變化的部分,抽象成接口照宝。

//飛行接口

?public interface FlyBehavior {

????????void fly();

? ? ? }

//叫聲接口

????public interface QuackBehavior {

????????void quack();

????}

? ? 3.2 超類中引用這兩個接口的變量蛇受,在對應的方法上調用接口的方法,提供空的構造函數(shù)厕鹃。子類在其構造函數(shù)上初始化對應行為的實現(xiàn)類兢仰,便具有其行為。

//父類抽象類

//父類抽象類

public abstract class Duck{

/**

? ? * 飛行行為接口

? ? */

? ? FlyBehaviorflyBehavior;

? ? /**

? ? * 叫聲行為接口

? ? */

? ? QuackBehaviorquackBehavior;

? ? /**

? ? * 提供空的構造函數(shù)熊响,以便子類調用,子類調用時旨别,new出來的行為是啥子類就是啥行為

? ? */

? ? public Duck(){

????}

????public void fly(){

????????flyBehavior.fly();

? ? }

????public void swim(){

????????System.out.println("I can swim");

? ? }

????public void quack(){

????????quackBehavior.quack();

? ? }

????public abstract void display();

? ? public void setFlyBehavior(FlyBehavior flyBehavior) {

????????this.flyBehavior = flyBehavior;

? ? }

????public void setQuackBehavior(QuackBehavior quackBehavior) {

????????this.quackBehavior = quackBehavior;

? ? }

}

? ? 子類實現(xiàn)類

public class GreenHeadDuckextends Duck {

????public GreenHeadDuck(){

????????flyBehavior =new GoodFlyBehavior();

? ? ? ? quackBehavior =new GaGaQuackBehavior();

? ? }

????@Override

? ? public void display() {

????????System.out.println("**GreenHeadDuck");

? ? }

}

若要添加新的飛行方式或者添加新的叫聲方式,只需寫個實現(xiàn)類實現(xiàn)FlyBehavior或QuackBehavior接口汗茄。若要添加新需求秸弛,添加會游泳的鴨子,只需新創(chuàng)建各SwimBehavior接口洪碳,提供實現(xiàn)類递览,并在超類上添加這個屬性,各子類需要游泳行為的瞳腌,在其構造函數(shù)上绞铃,給swimBehavior賦值響應的實現(xiàn)類即可。

4嫂侍、使用策略模式相比于面向對象的好處:

????新增行為簡單儿捧,行為類更好的復用,組合更方便挑宠。既有繼承帶來的復用好處菲盾,沒有挖坑。

5各淀、方法論:

當需要新的設計方式懒鉴,應對項目的可擴展性,降低復雜度:

1)分析項目變化與不變的部分,提取變化部分临谱,抽象成接口+實現(xiàn)

2)多用組合少用繼承璃俗;用行為類組合,而不是行為的繼承悉默。更有彈性城豁。

6、策略模式:

分別封裝行為接口麦牺,實現(xiàn)算法族钮蛛,超類里放行為接口對象,在子類里具體設定行為對象剖膳。

1)定義

策略模式是行為模式的一種魏颓,它對一系列的算法加以封裝,為算又的算法定義一個抽象的算法接口吱晒,并通過繼承該抽象算法接口對所有的算法加以封裝和實現(xiàn)甸饱,具體算法選擇交由客戶端決定。

2)原則

分離變化部分仑濒,封裝接口叹话,基于接口編程各種功能。此模式讓行為算法的變化獨立于算法的使用者墩瞳。

3)策略模式的角色和原則

? ? 1驼壶、Strategy:策略抽象(算法抽象),本例的算法抽象是行為接口(FlyBehavior)以及叫聲接口(QuackBehavior)

? ? 2喉酌、ConcreteStrategy:各種策略算法的具體實現(xiàn)热凹,本例的策略算法的具體實現(xiàn)是GoodFlyBehavior、BadFlyBehavior泪电、GuGuQuackBehavior般妙、GaGaQuackBehavior

? ? 3、Context:策略的外部封裝類相速,或者說是策略的容器類碟渺。根據(jù)不同的策略執(zhí)行不同的行為。策略由外部環(huán)境決定的突诬。本例中Context是GreenHeadDuck苫拍、RedHeadDuck

4)策略模式的優(yōu)點

? ? 1、策略模式提供了管理相關的算法族的辦法旺隙,策略類的登級結構定義了一個算法或行為族绒极。恰當?shù)厥褂美^承可以把公共的代碼移到父類里面,從而避免重復的代碼

? ? 2催束、策略模式提供了可以替換繼承關系的辦法。繼承可以處理多中算法或行為伏社。如果不是用策略模式抠刺,那么使用算法或行為的環(huán)境類就可能會有一些子類塔淤,每一個子類提供一個不同的算法或行為,但是速妖,這樣一來算法或行為的使用者和算法和行為本身混在了一起高蜂。決定使用哪一種算法或采取哪一種行為的邏輯就和算法或行為的邏輯混合在一起,從而不可能再獨立演化罕容。繼承使得動態(tài)改變算法或行為變得不可能备恤。

? ? 3、使用策略模式可以避免多重條件轉移語句锦秒。多重轉移語句不易維護露泊,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統(tǒng)統(tǒng)列在一個多重轉移語句里面旅择,比使用繼承的辦法還要原始和落后惭笑。

5)策略模式的缺點

? ? 1、客戶端必須知道所有的策略類生真,并自行決定使用哪一個策略類沉噩。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時選擇恰當?shù)乃惴愔啊Q言之川蒙,策略模式只適用于客戶端知道的所有的算法或行為的情況

? ? 2、策略模式造成很多的策略類长已。有時候可以通過把依賴于環(huán)境的狀態(tài)保存到客戶端里面畜眨。而將策略類設計成可共享的,這樣策略類實例可以被不同客戶端使用痰哨。換言之胶果,可以使用享元模式來減少對象的數(shù)量

7、Spring中使用的策略模式

1)spring資源訪問的方式


Resource接口是Spring資源訪問策略的抽象斤斧,它本身并不提供任何資源訪問實現(xiàn)早抠,具體資源訪問由該接口的實現(xiàn)類完成——每個實現(xiàn)類代表一種資源訪問。

Spring 為 Resource 接口提供了如下實現(xiàn)類:

????UrlResource:訪問網(wǎng)絡資源的實現(xiàn)類撬讽。

????ClassPathResource:訪問類加載路徑里資源的實現(xiàn)類蕊连。

????FileSystemResource:訪問文件系統(tǒng)里資源的實現(xiàn)類。

????ServletContextResource:訪問相對于 ServletContext 路徑里的資源的實現(xiàn)類:

????InputStreamResource:訪問輸入流資源的實現(xiàn)類游昼。

????ByteArrayResource:訪問字節(jié)數(shù)組資源的實現(xiàn)類甘苍。

????????從上面的例子中可以看到,使用策略模式可以讓客戶端在不同的飛行行為和叫聲行為中切換烘豌,但也有一個小小的不足载庭,客戶端代碼需要和不同的策略類耦合。

下面我們回到spring框架里,看看Spring框架的Context如何智能地選擇資源訪問策略囚聚。

ResourceLoader接口和ResourceLoaderAware接口

spring提供了兩個標志性接口:

? ? ResourceLoader:該接口的實現(xiàn)類的實例可以獲得一個Resource實例靖榕。

? ? ResourceLoaderAware:該接口的實現(xiàn)類的實例將獲得一個ResourceLoader實例。

在ResourceLoader接口里有如下方法:

????????Resource getResource(String location):該接口僅包含這個方法顽铸,該方法用于返回一個 Resource 實例茁计。ApplicationContext 的實現(xiàn)類都實現(xiàn) ResourceLoader 接口,因此 ApplicationContext 可用于直接獲取 Resource 實例谓松。

此處spring框架的ApplicationContext不僅是Spring容器星压,而且它還是資源訪問策略的"決策者",也就是策略模式中Context對象鬼譬,它將為客戶端代碼"智能"地選擇策略實現(xiàn)娜膘。

當ApplicationContext 實例獲取Resource實例時,系統(tǒng)將默認采用與ApplicationContext相同的資源訪問策略拧簸。對于如下代碼:

//通過ApplicationContext訪問資源

Resource res = ctx.getResource("application.xml");

從上面代碼中無法確定 Spring 將哪個實現(xiàn)類來訪問指定資源劲绪,Spring 將采用和 ApplicationContext 相同的策略來訪問資源。也就是說:如果 ApplicationContext 是 FileSystemXmlApplicationContext盆赤,res 就是 FileSystemResource 實例贾富;如果 ApplicationContext 是 ClassPathXmlApplicationContext,res 就是 ClassPathResource 實例牺六;如果 ApplicationContext 是 XmlWebApplicationContext颤枪,res 是 ServletContextResource 實例.

看如下示例程序,下面程序將使用 ApplicationContext 來訪問資源:

public static void main(String[] args) {

????ApplicationContext context =new ClassPathXmlApplicationContext("beans.xml");

? ? Resource res = context.getResource("beans.xml");

? ? System.out.println(res.getFilename());

? ? System.out.println(res.getDescription());

}

結果:

beans.xml

class path resource [beans.xml]

8淑际、阿里支付寶官方支付sdk中使用的策略模式

? ? 1)策略模式中Context對象是AlipayClient

? ? 2)策略模式中的Strategy對象是AlipayRequest畏纲、AlipayResponse

? ? ? ? ? ? 這兩個接口封裝了所有的支付寶請求、和返回值春缕。

? ? ? ? AlipayResponse代碼

public abstract class AlipayResponseimplements Serializable {

private static final long serialVersionUID =5014379068811962022L;

? ? @ApiField("code")

private String code;

? ? @ApiField("msg")

private String msg;

? ? @ApiField("sub_code")

private String subCode;

? ? @ApiField("sub_msg")

private String subMsg;

? ? private String body;

? ? private Map params;

? ? public AlipayResponse() {

}

/** @deprecated */

? ? @Deprecated

public String getErrorCode() {

return this.getCode();

? ? }

/** @deprecated */

? ? @Deprecated

public void setErrorCode(String errorCode) {

this.setCode(errorCode);

? ? }

public String getCode() {

return this.code;

? ? }

public void setCode(String code) {

this.code = code;

? ? }

public String getMsg() {

return this.msg;

? ? }

public void setMsg(String msg) {

this.msg = msg;

? ? }

public String getSubCode() {

return this.subCode;

? ? }

public void setSubCode(String subCode) {

this.subCode = subCode;

? ? }

public String getSubMsg() {

return this.subMsg;

? ? }

public void setSubMsg(String subMsg) {

this.subMsg = subMsg;

? ? }

public String getBody() {

return this.body;

? ? }

public void setBody(String body) {

this.body = body;

? ? }

public Map getParams() {

? ? ?return this.params;

? ? }

public void setParams(Map params) {

this.params = params;

? ? }

public boolean isSuccess() {

return StringUtils.isEmpty(this.subCode);

? ? }

}


AlipayRequest代碼

public interface AlipayRequest {

String getApiMethodName();

? ? Map getTextParams();

? ? String getApiVersion();

? ? void setApiVersion(String var1);

? ? String getTerminalType();

? ? void setTerminalType(String var1);

? ? String getTerminalInfo();

? ? void setTerminalInfo(String var1);

? ? String getProdCode();

? ? void setProdCode(String var1);

? ? String getNotifyUrl();

? ? void setNotifyUrl(String var1);

? ? String getReturnUrl();

? ? void setReturnUrl(String var1);

? ? Class getResponseClass();

? ? boolean isNeedEncrypt();

? ? void setNeedEncrypt(boolean var1);

? ? AlipayObject getBizModel();

? ? void setBizModel(AlipayObject var1);

}


? ? 3)ConcreteStrategy算法具體實現(xiàn)對象:AlipayTradePagePayRequest盗胀、AlipayTradePageResponse

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锄贼,隨后出現(xiàn)的幾起案子票灰,更是在濱河造成了極大的恐慌,老刑警劉巖宅荤,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屑迂,死亡現(xiàn)場離奇詭異,居然都是意外死亡冯键,警方通過查閱死者的電腦和手機惹盼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惫确,“玉大人手报,你說我怎么就攤上這事蚯舱。” “怎么了掩蛤?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵晓淀,是天一觀的道長。 經常有香客問我盏档,道長,這世上最難降的妖魔是什么燥爷? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任蜈亩,我火速辦了婚禮,結果婚禮上前翎,老公的妹妹穿的比我還像新娘稚配。我一直安慰自己,他們只是感情好港华,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布道川。 她就那樣靜靜地躺著,像睡著了一般立宜。 火紅的嫁衣襯著肌膚如雪冒萄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天橙数,我揣著相機與錄音尊流,去河邊找鬼。 笑死灯帮,一個胖子當著我的面吹牛崖技,可吹牛的內容都是我干的。 我是一名探鬼主播钟哥,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼迎献,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腻贰?” 一聲冷哼從身側響起吁恍,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎银受,沒想到半個月后践盼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡宾巍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年咕幻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顶霞。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡肄程,死狀恐怖锣吼,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情蓝厌,我是刑警寧澤玄叠,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站拓提,受9級特大地震影響读恃,放射性物質發(fā)生泄漏。R本人自食惡果不足惜代态,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一寺惫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹦疑,春花似錦西雀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至叁温,卻和暖如春再悼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膝但。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工帮哈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锰镀。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓娘侍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泳炉。 傳聞我的和親對象是個殘疾皇子憾筏,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容

  • 本章目錄如下: 一氧腰、階段一 二、階段二 三刨肃、策略模式說明 四古拴、本章用到的OO設計原則 五、模式問答 六真友、本章要點 ...
    黑夜0411閱讀 660評論 0 0
  • 設計模式 開題先說明一下黄痪,設計模式告訴我們如何組織類和對象以解決某種問題。讓代碼變得更加優(yōu)雅是我們責無旁貸的任務 ...
    tanghuailong閱讀 447評論 0 2
  • 一是嗜、需求引入 鴨子有不同的種類:綠頭鴨、紅頭鴨挺尾、橡皮鴨...鹅搪,它們都有相同點和不同點,相同點是它們都會游泳遭铺,不...
    Javar閱讀 374評論 0 0
  • 這幾天小車壞了丽柿,送進了維修廠! 然后我就沒有小車開了魂挂,真的去哪也不方便昂胶瘛! 到了維修的第三天锰蓬,我開始懷念起我的小車...
    君臨成長日記閱讀 226評論 0 0
  • 仙城微雨后,秋氣透窗紗眯漩。 枝上尋黃葉芹扭,風前數(shù)落花。 拈來云朵賦赦抖,拾得月輪斜舱卡。 莫負清涼意,閑添一盞茶队萤。
    巫小皇閱讀 283評論 0 3