天天看點

設計模式——政策模式

1.政策模式

定義一系列算法,把他們獨立封裝起來,并且這些算法之間可以互相替換。政策模式主要是管理一堆有共性的算法,政策模式讓算法獨立于使用它的客戶而變化,用戶端可以根據需要,很快切換這些算法,并且保持可擴充性。

政策模式的本質:分離算法,選擇實作。

2.UML類圖

政策模式結構中包括三種角色:

  • 政策(Strategy):政策是一個接口,該接口定義若幹個算法辨別,即定義了若幹個抽象方法。
  • 具體政策(ConcreteStrategy):具體政策是實作政策接口的類,具體政策實作政策接口所定義的抽象方法,即給出算法辨別的具體算法。
  • 上下文(Context):上下文是依賴于政策接口的類,即上下文包含有政策聲明的變量。上下文中提供一個方法,該方法委托政策變量調用具體政策所實作的政策接口中的方法。

3.舉例說明

就拿程式員舉例吧,假如現在我要寫一個程式,我們可以使用java、php。那到底使用哪種語言呢?使用我們的簡單工廠模式完全可以解決這個問題

1、抽象語言類

public interface CodeLanguage {
    void useLanguage();
}      

2、具體語言類

public class CodeJava implements CodeLanguage {
    @Override
    public void useLanguage() {
        System.out.println("使用Java語言程式設計");
    }
}      
public class CodePhp implements CodeLanguage {
    @Override
    public void useLanguage() {
        System.out.println("使用Php語言程式設計");
    }
}      

3、工廠類

public class LanguageFactory {
    public static CodeLanguage getLanguage(String type){
        CodeLanguage codeLanguage=null;
        switch (type){
            case "java":
                codeLanguage=new CodeJava();
                break;
            case "php":
                codeLanguage=new CodePhp();
                break;
        }
        return codeLanguage;
    }
}      

雖然問題解決了,但正如我們所說的簡單工廠模式的缺點一樣:

  • 工廠類負責所有對象的建立邏輯,該類出問題整個系統無法運作。
  • 系統擴充困難,一旦添加新産品就不得不修改工廠方法。
  • 由于使用了靜态工廠方法,是以工廠角色無法形成基于繼承的等級結構。

    假如我們又要增加使用python語言,那麼我們除了增加相應的語言類,還要修改工廠類,很明顯這不是最佳的方法,而政策模式便是最好的選擇。根據政策模式的定義,政策模式主要是管理一堆有共性的算法,這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統一的接口,根據“裡氏代換原則”和面向對象的多态性,用戶端可以選擇使用任何一個具體算法類,并隻需要維持一個資料類型是抽象算法類的對象。對比簡單工廠模式,我們發現,簡單工廠模式是利用工廠類根據參數來動态的生成具體産品類,然後單獨的調用具體産品類的方法;而政策模式是由用戶端建立這些具體算法類,然後交由上下文來調用這些具體算法類中的方法。

政策類接口

public interface CodeLanguage {
    void useLanguage();
}      

具體政策類

public class CodeJava implements CodeLanguage {
    @Override
    public void useLanguage() {
        System.out.println("使用Java語言程式設計");
    }
}      
public class CodePhp implements CodeLanguage {
    @Override
    public void useLanguage() {
        System.out.println("使用Php語言程式設計");
    }
}      

這點和簡單工廠模式是抽象産品和具體産品代碼是一樣的,下面是不同點,實作一個上下文對象

public class CodeContext {
    CodeLanguage codeLanguage;

    public CodeContext1(CodeLanguage codeLanguage) {
        this.codeLanguage = codeLanguage;
    }

    void useLanguage(){
        codeLanguage.useLanguage();
    }
}      

使用模式

public class Application {
    public static void main(String[] args) {
        CodeContext context;
        context = new CodeContext(new CodeJava());
        context.useLanguage();
        context = new CodeContext(new CodePhp());
        context.useLanguage();
    }
      

}

上面是一個最簡單的政策模式的實作方式,按照功能分為3個部分,定義政策抽象接口,然後根據具體算法實作政策接口,最後需要定義一個上下文對象。這裡的上下文對象主要用來切換算法,上下文對象裡面也是針對接口程式設計,具體算法實作被封裝了。

4.政策模式的了解

上面實作的隻是一種最簡單的政策模式的架構,實際應用的時候,我們可以針對不同情況修改上下文對象和具體的算法實作。比如說,可以增加一個抽象類實作作為算法模闆。抽象類裡面我們可以封裝一些公共的方法。這樣實作具體的算法的時候,每個算法公共部分就被分離出來。

政策模式的目的是把具體的算法抽離出來,把每個算法獨立出來。形成一系列有共同作用的算法組,然後這個算法組裡面的算法可以根據實際情況進行互相替換。

政策模式的中心不是如何實作這些算法,而是如何組織和調用這些算法。也就是把我們平時寫到一塊的算法解耦出來,獨立成一個子產品,增強程式的擴充性。

政策模式裡面的算法通常需要資料執行,我們可以根據實際情況把資料放在不同地方。例如可以放在上下文類裡面,然後每個算法都可以使用這些資料。或者對接口封裝一個抽象類,在抽象類裡面添加資料。這些可以根據實際的情況綜合考慮。設計模式裡面沒有一成不變的萬能模式,每種模式都有變化版本,需要根據實際的項目進行變通。

5.政策模式優缺點

政策模式的優點:

政策模式完全符合“開放-封閉原則”。

政策模式提供了管理相關算法族的辦法。恰當使用繼承可以把公共的代碼移到抽象政策類中,進而避免重複的代碼。

政策模式提供了一種可以替換繼承關系的辦法。如果不使用政策模式,那麼使用算法的環境類就可能會有一些子類,每一個子類提供一種不同的算法。但是,這樣一來算法的使用就和算法本身混在一起,不符合“單一職責原則”,而且使用繼承無法實作算法或行為在程式運作時的動态切換。

使用政策模式可以避免多重條件選擇語句。

更好的擴充性:在政策模式中擴充新的政策實作非常容易,隻要增加新的政策實作類,然後在選擇使用政策的地方選擇使用這個新的政策實作就好了。

政策模式缺點:

用戶端必須知道所有的政策類,并自行決定使用哪一個政策類,而且這樣也暴露了政策的具體實作。

由于政策模式把每個具體的政策實作都單獨封裝成為類,如果備選的政策很多的話,那麼對象的數目就會相應增多。

政策模式将造成系統産生很多具體政策類,任何細小的變化都将導緻系統要增加一個新的具體政策類。

政策模式的一系列算法地位是平等的,是可以互相替換的,事實上構成了一個扁平的算法結構,也就是在一個政策接口下,有多個平等的政策算法,就相當于兄弟算法。而且在運作時刻隻有一個算法被使用,這就限制了算法使用的層級,使用的時候不能嵌套使用。

6.模式優化

考慮到政策模式在使用的時候會過多的暴露政策類,代碼的耦合性過高,結合簡單工廠模式進行優化,對上下文類進行修改

public class CodeContext {
    CodeLanguage codeLanguage;

    public CodeContext(String type) {
        switch (type){
            case "java":
                CodeJava codeJava=new CodeJava();
                codeLanguage=codeJava;
                break;
                case "php":
                CodePhp codePhp=new CodePhp();
                codeLanguage=codePhp;
                break;
        }
    }

    void useLanguage(){
        codeLanguage.useLanguage();
    }
}
      
CodeContext context = new CodeContext("java");
context.useLanguage();
context = new CodeContext("php");
context.useLanguage();      

相比傳統政策模式,我們把直接建立具體上下文對象改通過參數判斷來生成,這樣暴露給用戶端的隻有上下文對象,代碼的耦合度更低,可以說這既是簡單工廠模式的更新,也是政策模式的更新。

參考:

http://mobile.51cto.com/ahot-418972.htm http://blog.csdn.net/lmj623565791/article/details/24116745 http://blog.sina.com.cn/s/blog_7f311ef50102uxqz.html http://blog.csdn.net/yanbober/article/details/45498567