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