版權聲明:本文爲博主原創文章,未經博主容許不得轉載java
PS:轉載請註明出處
做者: TigerChain
地址: http://www.jianshu.com/p/135532803cdb
本文出自 TigerChain 簡書 Android設計模式系列react
教程簡介android
正文git
好比說我要出行旅遊,那麼出行方式有--飛機、自駕遊、火車等,這幾種方式就是策略。再好比:某大型商場搞活動--滿 100 元送杯子,滿 300 減 50 ,滿 1000 元抽獎「一等將彩色電視機」,這種活動也是策略。在遊戲中,咱們打一個普通的怪使用普通的招便可,打大 BOSS 就要是用大招,這也是一種策略 ...github
就是對各個算法的一個封裝「不是實現算法,而是封裝算法」,讓客戶端很是容易的調用,省掉了客戶端 if else 噁心的判斷,讓客戶端獨立於各個策略算法
這裏舉一個簡單的例子:好比咱們在 Android 中必定會使用到 http 網絡請求,請求庫太多了,大概有 AsyncHttpclient,OkhttpClient,Volley 等「具體的策略」,那麼咱們徹底可使用一個策略模式,定義一個抽像策略,而後把各個請求策略封裝,客戶想使用哪一個就使用哪一個,很是靈活和方便設計模式
策略模式和簡單工廠很類似,確有不一樣,策略是一種行爲模式,而簡單工廠是建立型模式「建立對象」 後面再說安全
策略模式的定義服務器
策略是對算法的封裝,是一種形爲模式,將每個算法封裝到具備共同接口的獨立的類中,從而使得它們能夠相互替換網絡
策略的特色
策略模式的結構
角色 | 類別 | 說明 |
---|---|---|
Strategy | 抽象的策略 | 是一個接口或抽象類 |
ConcreteStrategy | 具體的策略類 | 實現了抽象的策略 |
Context | 一個普通的類 | 上下文環境,持有 Stragegy 的引用 |
策略模式簡單的 UML
一、曹操敗走華榮道
咱們知道三國演義中曹操敗走華容道的故事,相傳在赤壁之戰以後,曹操的船艦被劉備燒了,曹操逃離時面前有兩條路:一、平坦的大路。二、泥濘的華容道。面對這兩條路,曹操沒有選擇大路而選擇有炊煙的小路「華容道路」,理由---實則虛之,虛則實之,那麼曹操在選擇道路的時候其實就是選擇策略
敗走華容道的簡單的 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 圖實現代碼便可「很是簡單,相信均可以實現」
三、Android 中使用策略場景
段子來了
先看個段子,輕鬆一下「注如下只是一個簡單舉例,庫不分前後,俗話說沒有最好,只有最適合」
相信作 Android 的朋友都離不開網絡請求,有一天你「小明」發現了一個傳說中很好的網絡請求庫 AsyncHttpClient ,你高興的把網絡請求相關的 API 都實現了,經理看到了說不錯不錯,寫的很快嗎,忽然有一天,經理說:小明 AsyncHttpClient 好多 API 過期了「隨着 Android 版本的更新」,而且對 RestFul 支持的不太友好,我看到一個叫 Retorfit2「據說是最好的網絡」 的庫,默認支持 OkHttp ,用 Retorfit 把 AsyncHttpClient 替換了吧,很是簡單對你來講,小明這時估計內心飄過了一千匹羊駝「我靠,爲麻不早說」,又過了一些時間,經理又說,小明呀,Volley 是 Google 推薦的網絡請求庫,你換成 Volley 庫吧,小明此時估計把經理的八輩祖宗都問候了一遍,又是一通加班加點的改,最後 Happy 的改好了。後面又有一個牛 B 的庫,經理又讓替換,小明哭了「爲何受傷的老是我」...
看到這裏你們應該想到了,上面的請求場景就是一個個的策略,若是小明按照策略模式走下來,只是添加擴展子策略,壓根原來的方法毛都不用改,只能說,小明呀,你可張點心吧。
MVP + 策略模式
下面咱們使用 MVP + 策略模式模擬一個簡單的登陸功能,實現上面小明的需求
MVP+retorfit+rx 請求策略簡單的 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'
注: 如下代碼純粹是爲了演示策略模式,順手寫的,好多細節可能沒有考慮到,可是基本框架就是這樣的,能夠自行完善
/** * @Description MVP 中的 Presenter 基 * @Creator TigerChain(建立者) */ public interface Presenter { }
/** * @Description MVP 中的 Model 基類 * @Creator TigerChain(建立者) */ public interface Model { }
/** * @Description MVP 中的 View 基類 * @Creator TigerChain(建立者) */ public interface IView { }
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 就是一個抽象策略,這裏是登陸功能
具體策略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) ; }
/** * @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 帶策略的登陸功能就完成了。
下面來看客戶調用,不貼代碼了「放一張部分代碼截圖」,後面放出所有 DEMO 你們自行查看
怎麼樣,經過以上幾個例子,相信咱們對策略模式有了很好的理解了
demo 沒有實現完畢,其中 Retorfit 和 Volley 沒有完善,有興趣的能夠自行完善
Demo 地址:https://github.com/githubchen001/mvp-rx-loginStrategy
一、TimeInterpolator 時間插值器
作過動畫的朋友都知道,插值器的概念,一句話就是:設置不一樣的插值器,動畫能夠以不一樣的速度模型來執行
先看看 TimeInterpolator 和它的直接子類
TimeInterpolator 的 UML
從 UML 圖就能夠看出 TimeInterpolator 是一個典型的策略模式,你想使用那種插件器,是客戶端的事情,而且結合工廠模式建立各自的插件器
二、ListAdapter
乍一看好像沒有見過這個東東呀,可是我說一個你確定知道 ListView 知道吧,BaseAdapter「實現了 ListAdapter」 知道吧 ,你們之前確定都使用過 ListView 「雖然如今推薦使用 RecyclerView ,可是它依然被不少人使用」,它就是一個策略,咱們來分析一下
ListAdaper 和它的直接子類
ListAdapter 的簡單的 UML
以上只是 ListAdapter 簡單的一個 UML 圖「問題說明便可,真正的 ListAdapter 比這複雜多」,從上面能夠看到 ListAdapter 典型的一個策略模式,有興趣的朋友能夠自行跟一下源碼
三、RecyclerView.LayoutManager
RecyclerView.LayoutManager 和它的子類
RecyclerView.LayoutManager 簡單的 UML
能夠看到 RecyclerView.LayoutManager 也是一個策略模式
其實不知不覺中咱們使用了好多策略模式,只是沒有注意罷了,細細想一想,是否是那麼回事,再多例子再也不舉了。有興趣的朋友能夠自已去扒扒 Android 源碼看看那部分使用的是策略模式
策略模式和簡單工廠很是類似,結構基本上同樣,可是它們側重點不同
可是咱們在策略模式中可使用簡單工廠模式「把生成策略這一過程使用工廠去實現,這樣好很差呢?適合就是最好的」
既然策略模式使用這麼普遍,那麼策略模式是否是就是無敵了呢,沒有一點點缺點?確定不是的。
優勢:
缺點:
到此爲止咱們簡單明瞭的介紹完了策略模式,最後說一下:點贊是一種美德