人人都會設計模式---策略模式--Strategy

策略模式大綱

版權聲明:本文爲博主原創文章,未經博主容許不得轉載java

PS:轉載請註明出處
做者: TigerChain
地址: http://www.jianshu.com/p/135532803cdb
本文出自 TigerChain 簡書 Android設計模式系列react

教程簡介android

  • 一、閱讀對象
    本篇教程適合新手閱讀,老手直接略過
  • 二、教程難度
    初級,本人水平有限,文章內容不免會出現問題,若是有問題歡迎指出,謝謝
  • 三、Demo 地址
    https://github.com/githubchen001/DesignPattern 請看 Strategy 部分

正文git

1、什麼是策略模式

一、 生活中的策略

好比說我要出行旅遊,那麼出行方式有--飛機、自駕遊、火車等,這幾種方式就是策略。再好比:某大型商場搞活動--滿 100 元送杯子,滿 300 減 50 ,滿 1000 元抽獎「一等將彩色電視機」,這種活動也是策略。在遊戲中,咱們打一個普通的怪使用普通的招便可,打大 BOSS 就要是用大招,這也是一種策略 ...github

二、程序中的策略

就是對各個算法的一個封裝「不是實現算法,而是封裝算法」,讓客戶端很是容易的調用,省掉了客戶端 if else 噁心的判斷,讓客戶端獨立於各個策略算法

這裏舉一個簡單的例子:好比咱們在 Android 中必定會使用到 http 網絡請求,請求庫太多了,大概有 AsyncHttpclient,OkhttpClient,Volley 等「具體的策略」,那麼咱們徹底可使用一個策略模式,定義一個抽像策略,而後把各個請求策略封裝,客戶想使用哪一個就使用哪一個,很是靈活和方便設計模式

策略模式和簡單工廠很類似,確有不一樣,策略是一種行爲模式,而簡單工廠是建立型模式「建立對象」 後面再說安全

策略模式的定義服務器

策略是對算法的封裝,是一種形爲模式,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換網絡

策略的特色

  • 是一種行爲模式,對算法封裝,使得客戶端獨立於各個策略
  • 擴展性強,添加策略無非就是添加一個具體的實現類而已,代價很是低

策略模式的結構

角色 類別 說明
Strategy 抽象的策略 是一個接口或抽象類
ConcreteStrategy 具體的策略類 實現了抽象的策略
Context 一個普通的類 上下文環境,持有 Stragegy 的引用

策略模式簡單的 UML

策略模式簡單的 UML

2、策略模式舉例

一、曹操敗走華榮道

咱們知道三國演義中曹操敗走華容道的故事,相傳在赤壁之戰以後,曹操的船艦被劉備燒了,曹操逃離時面前有兩條路:一、平坦的大路。二、泥濘的華容道。面對這兩條路,曹操沒有選擇大路而選擇有炊煙的小路「華容道路」,理由---實則虛之,虛則實之,那麼曹操在選擇道路的時候其實就是選擇策略

敗走華容道的簡單的 UML

敗走華榮道

根據 UML 編碼

  • 一、定義一個路的抽象策略
/**
 * 抽象的策略,定義逃跑路線
 */
public interface IRunStrategy {
    // 逃跑線路
    void escapeRoute() ;
}
  • 二、定義具體的路徑--大路
/**
 * 具體的策略一走大路
 */
public class Highroad implements IRunStrategy {
    @Override
    public void escapeRoute() {
        System.out.println("走大路");
    }
}
  • 三、定義具體路線--華容道
/**
 * 具體的策略二走華容道
 */
public class HuaRongRoad implements IRunStrategy {
    @Override
    public void escapeRoute() {
        System.out.println("走華容道");
    }
}
  • 四、定義上下文,選擇方式
/**
 * 上下文 持有 IRunStrategy 的引用
 */
public class ContextRunStrategy {

    private IRunStrategy iRunStrategy ;

    public ContextRunStrategy(IRunStrategy iRunStrategy){
        this.iRunStrategy = iRunStrategy ;
    }

    /**
     * 選擇道路
     */
    public void choiceRoad(){
        iRunStrategy.escapeRoute();
    }
}
  • 五、主角曹操登場,看看曹操是如何選擇道路的
/**
 * 曹操選擇路線
 */
public class CaoCao {

    public static void main(String args[]){
        /**
         * 曹操疑心重,選擇了華容道,對曹操來講至於雜樣走華容道,不關心,死多少人也不關心,只關心我要走這條道就好
         */
        IRunStrategy huaRongRoad = new HuaRongRoad() ;
        ContextRunStrategy contextRunStrategy = new ContextRunStrategy(huaRongRoad) ;
        contextRunStrategy.choiceRoad();
    }
}

真的走了華容道,好吧 no zuo no die ,咱們能夠看到上面曹操選擇逃跑路線都是行爲,因此很適合策略模式「策略模式就是一種選擇模式,當你猶豫不定的時候就使用策略模式」

注意: 策略的核心不是如何實現算法,而是如何更優雅的把這些算法組織起來,讓客戶端很是好調用「雖然策略很是多,能夠自由切換,可是同一時間客戶端只能調用一個策略,其實也很好理解,你不可能同時既坐飛機,又坐火車」。

二、出行旅行方式

通過上面的曹操敗走華榮道,咱們對策略有了感受了吧,那麼下來咱們趁熱打鐵,再來一發,咱們都知道出去旅行通常方式:坐飛機、坐火車、坐大巴、自駕遊等等,這一個個的出行方式就是策略,接下來我給出簡單的 UML 圖,代碼部分請各自自行實現「道理都懂,你的生活質量仍是沒有提升,方法再多也不見有多成功,就是由於實踐太少,動手纔是真理,靠--忘記吃藥了,脈動回來」

出行方式簡單的 UML

出行策略 UML

代碼實現

你們根據出行的 UML 圖實現代碼便可「很是簡單,相信均可以實現」

三、Android 中使用策略場景

段子來了

先看個段子,輕鬆一下「注如下只是一個簡單舉例,庫不分前後,俗話說沒有最好,只有最適合」

相信作 Android 的朋友都離不開網絡請求,有一天你「小明」發現了一個傳說中很好的網絡請求庫 AsyncHttpClient ,你高興的把網絡請求相關的 API 都實現了,經理看到了說不錯不錯,寫的很快嗎,忽然有一天,經理說:小明 AsyncHttpClient 好多 API 過期了「隨着 Android 版本的更新」,而且對 RestFul 支持的不太友好,我看到一個叫 Retorfit2「據說是最好的網絡」 的庫,默認支持 OkHttp ,用 Retorfit 把 AsyncHttpClient 替換了吧,很是簡單對你來講,小明這時估計內心飄過了一千匹羊駝「我靠,爲麻不早說」,又過了一些時間,經理又說,小明呀,Volley 是 Google 推薦的網絡請求庫,你換成 Volley 庫吧,小明此時估計把經理的八輩祖宗都問候了一遍,又是一通加班加點的改,最後 Happy 的改好了。後面又有一個牛 B 的庫,經理又讓替換,小明哭了「爲何受傷的老是我」...

看到這裏你們應該想到了,上面的請求場景就是一個個的策略,若是小明按照策略模式走下來,只是添加擴展子策略,壓根原來的方法毛都不用改,只能說,小明呀,你可張點心吧。

MVP + 策略模式

下面咱們使用 MVP + 策略模式模擬一個簡單的登陸功能,實現上面小明的需求

MVP+retorfit+rx 請求策略簡單的 UML

MVP+策略登陸 UML

根據 UML 擼碼

首先咱們要使用 AsyncHttpClient、Retorfit 等,先添加配置 Gradle「項目 Module 的 build.grade中」

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.1.5'
compile 'com.loopj.android:android-async-http:1.4.9'

注: 如下代碼純粹是爲了演示策略模式,順手寫的,好多細節可能沒有考慮到,可是基本框架就是這樣的,能夠自行完善

  • 一、分別新建 MVP 的基類接口,IPresenter,Model,IView
/**
 * @Description MVP 中的 Presenter 基
 * @Creator TigerChain(建立者)
 */
public interface Presenter {
}
/**
 * @Description MVP 中的 Model 基類
 * @Creator TigerChain(建立者)
 */
public interface Model {
}
/**
 * @Description MVP 中的 View 基類
 * @Creator TigerChain(建立者)
 */
public interface IView {
}
  • 二、新建 MVP 的關聯接口 ILoginContact.java 「固然也能夠不寫此類,直接寫登陸 MVP 的直接子類」
package designpattern.tigerchain.com.mvphttpstrategy.mvp;

import designpattern.tigerchain.com.mvphttpstrategy.mvp.domain.User;
import io.reactivex.Observable;

/**
 * @Description MVP 的關聯類「也能夠單首創建 MVP 就是有點亂」
 * @Creator TigerChain(建立者)
 */
public interface ILoginContact {

    interface LoginView extends IView{
        //顯示進度條
        void showProgress() ;
        //隱藏進度條
        void hideProgress() ;
        //登陸成功
        void loadSuccess(String str) ;
        //登陸失敗
        void loadFailed(String str) ;
        //取得用戶名
        String getUserName() ;
        //取得用戶密碼
        String getUserPass() ;
        //清除輸入框
        void clearEditText() ;
        //用戶名和密碼不能爲空
        void editnotNull() ;
    }

    interface LoginPresenter extends Presenter{類
        /**
         * 登陸功能
         */
        void login() ;

        /**
         * 清除輸入框架內容
         */
        void clear() ;
    }

    interface ILoginModel extends Model{
        /***
         * 登陸的方法,其實這裏就是一個抽象策略,至於你使用 retrofit 仍是 asynchttpClient 仍是 Volley 那是本身的事情
         * @param uName
         * @param uPass
         * @return
         */
        Observable<User> login(String uName, String uPass) ;
    }
}

其中 ILoginModel 就是一個抽象策略,這裏是登陸功能

  • 三、分別實現具體的策略「使用不一樣的網絡請求庫調用登陸 API」

具體策略1:使用 AsyncHttpClient 調用登陸

/**
 * @Description 具體策略使用 AsyncHttpClient 來調用登陸 API
 * @Creator TigerChain(建立者)
 */
public class AsynchHppClientImplLogimModel implements ILoginContact.ILoginModel {

    @Override
    public Observable<User> login(final String uName, final String uPass) {
        return Observable.create(new ObservableOnSubscribe<User>() {
            @Override
            public void subscribe(final ObservableEmitter<User> e) throws Exception {

                AsyncHttpClient client = new AsyncHttpClient() ;
                // 這裏就是一個請求 沒有真正的對接服務器,只是一個演示
                client.get("http://www.baidu.com", new AsyncHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {

                        if(uName.equalsIgnoreCase("TigerChain") && uPass.equals("123")){
                            User user = new User() ;
                            user.setuName(uName);
                            user.setUpass(uPass);
                            e.onNext(user);
                            e.onComplete();
                        }else{
                            e.onNext(null);
                            e.onComplete();
                        }
                    }

                    @Override
                    public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
                        e.onError(error);
                    }
                }) ;
            }
        });
    }
}

具體策略2:使用 Volley 調用登陸 API

/**
 * @Description 具體策略使用 Volley 實現登陸功能
 * @Creator TigerChain(建立者)
 */
public class VolleyImplLoginModel implements ILoginContact.ILoginModel {

    @Override
    public Observable<User> login(final String uName, final String uPass) {
        return Observable.create(new ObservableOnSubscribe<User>() {
            @Override
            public void subscribe(final ObservableEmitter<User> e) throws Exception {

                /***
                 * 這裏調用和 Volley 相關的 API 實現登陸便可
                 */
            }
        });
    }
}

具體策略3:使用 RetorFit 調用登陸 API

/**
 * @Description 具體策略 使用 RetorFit 實現登陸功能性
 * @Creator TigerChain(建立者)
 */
public class RetorFitImplLoginModel implements ILoginContact.ILoginModel {

    @Override
    public Observable<User> login(final String uName, final String uPass) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://")
                .build();
        ILoginRetorfitApi loginService = retrofit.create(ILoginRetorfitApi.class) ;
        return loginService.login(uName,uPass) ;
    }
}

其中 User 和 ILoginRetorfitApi 類分別是:

# User.java

/**
 * @Description 普通人的 Java
 * @Creator TigerChain(建立者)
 */
public class User {

    private String uName ;
    private String Upass ;

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    public String getUpass() {
        return Upass;
    }

    public void setUpass(String upass) {
        Upass = pass;
    }
}
# ILoginRetorfitApi.java
/**
 * @Description Retrofit API
 * @Creator TigerChain(建立者)
 */
public interface ILoginRetorfitApi {

    @GET("/login")
    Observable<User> login( @Field("userName") String userName,
                            @Field("passWord")String passWord) ;
}
  • 四、策略中的上下文「這裏就是咱們具體的 P」 LoginPresenterImpl.java
/**
 * @Description MVP 中的P ,就至關於策略中Context
 * @Creator junjun(建立者)
 */
public class LoginPresenterImpl implements ILoginContact.LoginPresenter {

    private ILoginContact.ILoginModel iLoginModel ;
    private ILoginContact.LoginView loginView ;

    public LoginPresenterImpl(ILoginContact.LoginView loginView,ILoginContact.ILoginModel iLoginModel){
        this.iLoginModel = iLoginModel ;
        this.loginView = loginView ;
    }

    @Override
    public void login() {

        String uName = loginView.getUserName() ;
        String uPass = loginView.getUserPass() ;

        if(TextUtils.isEmpty(uName) || TextUtils.isEmpty(uPass)){
            loginView.editnotNull();
            return ;
        }
        loginView.showProgress();
        iLoginModel.login(uName,uPass)
//                subscribeOn(Schedulers.io()) 因爲 AsyncHttpClient 自己就是在子線程去請求的,因此這裏爲了演示把這個去掉
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<User>() {

                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(User user) {
                        loginView.loadSuccess("登陸成功");

                    }

                    @Override
                    public void onError(Throwable e) {
                        loginView.loadFailed("用戶名或密碼錯誤,登陸失敗");
                        loginView.hideProgress();
                    }

                    @Override
                    public void onComplete() {
                        loginView.hideProgress();
                    }
                }) ;
    }

    @Override
    public void clear() {
        loginView.clearEditText();
    }
}

到此爲止,咱們的 MVP+RX+Retorfit 帶策略的登陸功能就完成了。

  • 五、客戶端調用「在 Activity 中調用」

下面來看客戶調用,不貼代碼了「放一張部分代碼截圖」,後面放出所有 DEMO 你們自行查看

mvpLoginStrategy.png

怎麼樣,經過以上幾個例子,相信咱們對策略模式有了很好的理解了

  • 六、最後運行看一下

login.gif

demo 沒有實現完畢,其中 Retorfit 和 Volley 沒有完善,有興趣的能夠自行完善

Demo 地址:https://github.com/githubchen001/mvp-rx-loginStrategy

3、Android 源碼中的策略模式

一、TimeInterpolator 時間插值器

作過動畫的朋友都知道,插值器的概念,一句話就是:設置不一樣的插值器,動畫能夠以不一樣的速度模型來執行

先看看 TimeInterpolator 和它的直接子類

TimeInterpolator 和它的子類

TimeInterpolator 的 UML

TimeInterpolator 簡單的 UML

從 UML 圖就能夠看出 TimeInterpolator 是一個典型的策略模式,你想使用那種插件器,是客戶端的事情,而且結合工廠模式建立各自的插件器

二、ListAdapter

乍一看好像沒有見過這個東東呀,可是我說一個你確定知道 ListView 知道吧,BaseAdapter「實現了 ListAdapter」 知道吧 ,你們之前確定都使用過 ListView 「雖然如今推薦使用 RecyclerView ,可是它依然被不少人使用」,它就是一個策略,咱們來分析一下

ListAdaper 和它的直接子類

ListAdapter 和它的子類

ListAdapter 的簡單的 UML

ListAdapter 簡單的 UML

以上只是 ListAdapter 簡單的一個 UML 圖「問題說明便可,真正的 ListAdapter 比這複雜多」,從上面能夠看到 ListAdapter 典型的一個策略模式,有興趣的朋友能夠自行跟一下源碼

三、RecyclerView.LayoutManager

RecyclerView.LayoutManager 和它的子類

RecyclerView.LayoutManager 和它的子類

RecyclerView.LayoutManager 簡單的 UML

RecyclerView.LayoutManager 簡單的 UML

能夠看到 RecyclerView.LayoutManager 也是一個策略模式

其實不知不覺中咱們使用了好多策略模式,只是沒有注意罷了,細細想一想,是否是那麼回事,再多例子再也不舉了。有興趣的朋友能夠自已去扒扒 Android 源碼看看那部分使用的是策略模式

4、策略模式和簡單工廠模式

策略模式和簡單工廠很是類似,結構基本上同樣,可是它們側重點不同

  • 策略模式:是一個行爲模式,解決策略的切換和擴展,讓策略獨立於客戶端
  • 簡單工廠模式:是一種建立模式「建立對象」,接收指令建立出具體的對象,讓對象的建立和具體的使用客戶無關

可是咱們在策略模式中可使用簡單工廠模式「把生成策略這一過程使用工廠去實現,這樣好很差呢?適合就是最好的」

5、策略模式的優缺點

既然策略模式使用這麼普遍,那麼策略模式是否是就是無敵了呢,沒有一點點缺點?確定不是的。

優勢:

  • 一、結構清晰,把策略分離成一個個單獨的類「替換了傳統的 if else」
  • 二、代碼耦合度下降,安全性提升「各個策略的細節被屏蔽」

缺點:

  • 一、客戶端必需要知道全部的策略類,不然你不知道該使用那個策略,因此策略模式適用於提早知道全部策略的狀況下
  • 二、增長了類的編寫,原本只須要 if else 便可「可是這是全部模式和架構的通病呀」

到此爲止咱們簡單明瞭的介紹完了策略模式,最後說一下:點贊是一種美德

相關文章
相關標籤/搜索