設計模式-策略模式

 定義: 策略模式是一種定義一系列算法的方法,從概念上來看,全部這些算法完成的都是相同的工做,只是實現不一樣,它能夠以相同的方式調用全部的算法,減小各類算法類與使用算法類之間的耦合關係。java

    策略模式主要用一個類(context環境類)來承接上下文,配置維護一個對全部算法父類對象的引用。算法

    策略模式遵循的結構圖以下:架構

     

Context(應用場景): 
     1 須要使用ConcreteStrategy提供的算法。 
    2 內部維護一個Strategy的實例。 
    3 負責動態設置運行時Strategy具體的實現算法。 
    4 負責跟Strategy之間的交互和數據傳遞。 
Strategy(抽象策略類): 
    1 定義了一個公共接口,各類不一樣的算法以不一樣的方式實現這個接口,Context使用這個接口調用不一樣的算法,通常使用接口或抽象類實現。 
ConcreteStrategy(具體策略類): 
    1 實現了Strategy定義的接口,提供具體的算法實現。

下面列舉一個開發過程當中的實例進行講解:
    模塊功能:UI界面經過不一樣的操做詳細向服務端發送請求,而且處理相應結果。
    1.請求apk信息,發送給UI。
    2.請求apk、圖片下載。
    3.請求應用版本,通知UI是否能夠更新。
    4.請求查詢信息。ide

對於上面同一個模塊功能,下面將列舉四個例子來進行比較:
    (1)常規的操做方法。
    2)策略模式實現
    (3)策略模式+簡單工廠
    (4)策略模式+反射機制this

1)通常代碼架構情形:
    枚舉類型:   RequestChoise------請求類型
    UI客戶端:   UIClient-------------請求發起、調用者
    枚舉類型RequestChoise.java 
url

package com.zlc.all;
 
public enum RequestChoise {
        REQUEST_APK_MSG,//請求apk詳情
        REQEUST_APK_IMG_DOWNLOAD,//請求下載
        REQUEST_VERSION,//請求版本號
        REQUEST_SEARCH_MSG//請求查詢信息
}

   UIClient.java spa

package com.zlc.nomal;
 
import com.zlc.all.RequestChoise;
 
public class UIClient {
    String  msg = "";
    public void questMsg(RequestChoise choise,String url){
        switch (choise) {
        case REQUEST_APK_MSG:
            System.out.println("1:請求apk信息");
            System.out.println("2:把數據通知給UI");
            System.out.println("3:…………");
            break;
        case REQEUST_APK_IMG_DOWNLOAD:
            System.out.println("1:請求apk/圖片下載");
            System.out.println("2:把數據保存到文件");
            System.out.println("3:通知UI已經下載成功或失敗");
            System.out.println("4:…………");
            break;
        case REQUEST_VERSION:
            System.out.println("1:請求該應用版本信息");
            System.out.println("2:與自己應用的版本進行比較");
            System.out.println("3:通知UI顯示是否更新對話框");
            System.out.println("4:…………………………");
            break;
        case REQUEST_SEARCH_MSG:
            System.err.println("1:請求搜索框輸入的信息");
            System.out.println("2:獲取到的信息進行分頁提示UI顯示");
            System.out.println("3:……………………");
            break;
        default:
            break;
        }
    }
}

這樣寫缺點:  1.客戶端代碼量多、繁雜。 
                   2.客戶端耦合性高,涉及到多個類。 
                   3.客戶端重複代碼較多如:屢次調用.net


2)使用策略模式:
    剛咱們提到策略模式涉及到三個角色:
    環境(Context)角色:持有一個Strategy類的引用。 
    抽象策略(Strategy)角色:這是一個抽象角色,一般由一個接口或抽象類實現。此角色給出全部的具體策略類所需的接口。 
    具體策略(ConcreteStrategy)角:包裝了相關的算法或行爲。     
    
(1)抽象算法類:Request-------------提供公共接口
(2)具體算法類RequestAPKMsg----請求apk詳細信息
       具體算法類:RequestDownload--請求下載資源
       具體算法類:RequestSearchMsg-請求搜索結果
       具體算法類:RequestVersion-----請求版本信息
(3)環境角色類:RequestContext------持有一個Request類的引用,提供具體算法調用接口
       枚舉類型:   RequestChoise------請求類型
       UI客戶端:   UIClient-------------請求發起、調用者設計


    Request.java    code

package com.zlc.all;
/**
 * 抽象算法類:Request
 * 提供公共的請求接口
 * **/
public interface Request {
     
    public void requestFromeServer(String url);
}

RequestAPKMsg.java 

package com.zlc.all;
 
/**
 * 具體算法類:APKMsgRequest
 * 請求apk詳細信息
 **/
public class RequestAPKMsg implements Request {
 
    @Override
    public void requestFromeServer(String url) {
        // TODO Auto-generated method stub
        System.out.println("1:請求apk信息");
        System.out.println("2:把數據通知給UI");
        System.out.println("3:…………");
    }
 
}

RequestDownload.java 

package com.zlc.all;
/**
 * 具體算法類:RequestDownload
 * 請求下載資源
 **/
public class RequestDownload implements Request{
    @Override
    public void requestFromeServer(String url) {
        // TODO Auto-generated method stub
        System.out.println("1:請求apk/圖片下載");
        System.out.println("2:把數據保存到文件");
        System.out.println("3:通知UI已經下載成功或失敗");
        System.out.println("4:…………");
    }
 
}

RequestSearchMsg.java 

package com.zlc.all;
/**
 * 具體算法類:RequestSearchMsg
 * 請求搜索結果
 * **/
public class RequestSearchMsg implements Request{
    @Override
    public void requestFromeServer(String url) {
        // TODO Auto-generated method stub
        System.err.println("1:請求搜索框輸入的信息");
        System.out.println("2:獲取到的信息進行分頁提示UI顯示");
        System.out.println("3:……………………");
    }
 
}

 RequestVersion.java

package com.zlc.all;
/**
 * 具體算法類:RequestVersion
 * 請求版本信息
 **/
public class RequestVersion implements Request{
    @Override
    public void requestFromeServer(String url) {
        // TODO Auto-generated method stub
        System.out.println("1:請求該應用版本信息");
        System.out.println("2:與自己應用的版本進行比較");
        System.out.println("3:通知UI顯示是否更新對話框");
        System.out.println("4:…………………………");
    }
 
}

RequestContext.java

package com.zlc.stratery;
 
import com.zlc.all.Request;
/**
 * 環境角色類:RequestContex
 * 持有一個Request類的引用
 **/
public class RequestContext {
    private Request request;
    public RequestContext(Request request) {
        // TODO Auto-generated constructor stub
        this.request = request;
    }
    public void requestFromServer(String url){
        request.requestFromServer(url);
    }
}

UIClient.java 

package com.zlc.stratery;
 
import com.zlc.all.RequestAPKMsg;
import com.zlc.all.RequestChoise;
import com.zlc.all.RequestDownload;
import com.zlc.all.RequestSearchMsg;
import com.zlc.all.RequestVersion;
 
public class UIClient {
    String  msg = "";
    RequestContext context;
    public void questMsg(RequestChoise choise,String url){
        switch (choise) {
        case REQUEST_APK_MSG:
            context = new RequestContext(new RequestAPKMsg());
            break;
        case REQEUST_APK_IMG_DOWNLOAD:
            context = new RequestContext(new RequestDownload());
            break;
        case REQUEST_VERSION:
            context = new RequestContext(new RequestVersion());
            break;
        case REQUEST_SEARCH_MSG:
            context = new RequestContext(new RequestSearchMsg());
            break;
        default:
            break;
        }
        context.requestFromServer(url);
    }
}

策略模式優勢: 
    (1)策略模式提供了管理相關的算法族的辦法,策略類的等級結構定義了一個算法或行爲族,恰當的使用繼承能夠把公共的代碼移植帶父類裏面去,從而減小了代碼的重複。 
    (2)策略模式提供了能夠替換繼承關係的辦法。繼承能夠處理多種算法或行爲。若是不是用策略模式,那麼使用算法或行爲的環境類就可能會有一些子類(使用環境類一個子類對應一種算法類),每個子類提供一個不一樣的算法或行爲。可是,這樣一來算法或行爲的使用者就和算法或行爲自己混在一塊兒。決定使用哪種算法或採起哪種行爲的邏輯就和算法或行爲的邏輯混合在一塊兒,從而不可能再獨立演化。繼承使得動態改變算法或行爲變得不可能。 
    (3)使用策略模式能夠避免使用多重條件轉移語句(if……else)。多重轉移語句不易維護,它把採起哪種算法或採起哪種行爲的邏輯與算法或行爲的邏輯混合在一塊兒,通通列在一個多重轉移語句裏面,比使用繼承的辦法還要原始和落後。
缺點: 
    (1)客戶端必須知道全部的策略類( 下面經過簡單工廠+策略模式解決這個問題),並自行決定使用哪個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。換言之,策略模式只適用於客戶端知道全部的算法或行爲的狀況( 能夠經過反射解決這個問題)。 

    (2)策略模式形成不少的策略類。有時候能夠經過把依賴於環境的狀態保存到客戶端裏面,而將策略類設計成可共享的,這樣策略類實例能夠被不一樣客戶端使用。換言之,能夠使用享元模式來減小對象的數量。

3)簡單工廠+策略模式
    把客戶端的方法(switch部分)移植到環境類裏面去。
    環境角色類RequestContext------持有一個Request類的引用,提供具體算法調用接口
    UI客戶端:   UIClient-------------請求發起、調用者
    其餘同上

    RequestContext

package com.zlc.strategy.factory;
 
import com.zlc.all.Request;
import com.zlc.all.RequestAPKMsg;
import com.zlc.all.RequestChoise;
import com.zlc.all.RequestDownload;
import com.zlc.all.RequestSearchMsg;
import com.zlc.all.RequestVersion;
/**
 * 環境角色類:RequestContex
 * 持有一個Request類的引用
 **/
public class RequestContext {
    private Request request;
    public RequestContext(RequestChoise choise) {
        switch (choise) {
        case REQUEST_APK_MSG:
            request = new RequestAPKMsg();
            break;
        case REQEUST_APK_IMG_DOWNLOAD:
            request = new RequestDownload();
            break;
        case REQUEST_VERSION:
            request = new RequestVersion();
            break;
        case REQUEST_SEARCH_MSG:
            request = new RequestSearchMsg();
            break;
        default:
            break;
        }
    }
    public void requestFromServer(String url){
        request.requestFromServer(url);
    }
}

UIClietn.java 

package com.zlc.strategy.factory;
 
import com.zlc.all.RequestChoise;
 
public class UIClient {
    String  msg = "";
    RequestContext context;
    public void questMsg(RequestChoise choise,String url){
        context = new RequestContext(choise);
        context.requestFromServer(url);
    }
}

這樣客戶端的代碼就更少了,也大大下降了代碼的耦合性,客戶端不須要涉及詳細的算法類,更方便與維護。


4)反射+策略模式
    環境角色類RequestContext------持有一個Request類的引用,提供具體算法調用接口
    UI客戶端:   UIClient-------------請求發起、調用者
    其餘同上

    RequestContext.java

package com.zlc.reflect;
 
import com.zlc.all.Request;
/**
 * 環境角色類:RequestContex
 * 持有一個Request類的引用
 **/
public class RequestContext {
    private Request request;
    public RequestContext(String fileName) {
        // TODO Auto-generated constructor stub
        Class clazz ;
        try {
            clazz = Class.forName(fileName);
            request = (Request)clazz.newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
    public void requestFromServer(String url){
        request.requestFromServer(url);
    }
}

 UIClient.java 

package com.zlc.reflect;
 
 
public class UIClient {
    String  msg = "";
    RequestContext context;
    public void questMsg(String url,String fileName){
        context = new RequestContext(fileName);
        context.requestFromServer(url);
    }
}

這樣,選擇分支結構也去掉了。 

策略模式(Strategy):它定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化,不會影響到使用算法的用戶。

最後說一下策略模式和簡單工廠之間的區別:    (1)策略模式的具體算法類對象是在使用者類裏面進行建立的,而後傳遞給環境類(管理一個父類算法對象),簡單工廠是經過使用者提供的條件在工廠類裏面建立。    (2)策略模式具體是算法行爲是經過環境類Context提供的接口調用,簡單工廠是經過返回給調用者所須要的對象,而後由返回對象直接調用。

相關文章
相關標籤/搜索