人人都会设计模式:03-策略模式--Strategy
版權聲明:本文為博主原創文章,未經博主允許不得轉載
公眾號:TigerChain
歡迎關注有更多文章等你來讀
教程簡介
- 1、閱讀對象
本篇教程適合新手閱讀,老手直接略過 - 2、教程難度
初級,本人水平有限,文章內容難免會出現問題,如果有問題歡迎指出,謝謝 - 3、Demo 地址
https://github.com/githubchen001/DesignPattern 請看 Strategy 部分
正文
一、什么是策略模式
1、 生活中的策略
比如說我要出行旅游,那么出行方式有--飛機、自駕游、火車等,這幾種方式就是策略。再比如:某大型商場搞活動--滿 100 元送杯子,滿 300 減 50 ,滿 1000 元抽獎「一等將彩色電視機」,這種活動也是策略。在游戲中,我們打一個普通的怪使用普通的招即可,打大 BOSS 就要是用大招,這也是一種策略 ...
2、程序中的策略
就是對各個算法的一個封裝「不是實現算法,而是封裝算法」,讓客戶端非常容易的調用,省掉了客戶端 if else 惡心的判斷,讓客戶端獨立于各個策略
這里舉一個簡單的例子:比如我們在 Android 中一定會使用到 http 網絡請求,請求庫太多了,大概有 AsyncHttpclient,OkhttpClient,Volley 等「具體的策略」,那么我們完全可以使用一個策略模式,定義一個抽像策略,然后把各個請求策略封裝,客戶想使用哪個就使用哪個,非常靈活和方便
策略模式和簡單工廠很相似,確有不同,策略是一種行為模式,而簡單工廠是創建型模式「創建對象」 后面再說
策略模式的定義
策略是對算法的封裝,是一種形為模式,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換
策略的特點
- 是一種行為模式,對算法封裝,使得客戶端獨立于各個策略
- 擴展性強,添加策略無非就是添加一個具體的實現類而已,代價非常低
策略模式的結構
| Strategy | 抽象的策略 | 是一個接口或抽象類 |
| ConcreteStrategy | 具體的策略類 | 實現了抽象的策略 |
| Context | 一個普通的類 | 上下文環境,持有 Stragegy 的引用 |
策略模式簡單的 UML
二、策略模式舉例
1、曹操敗走華榮道
我們知道三國演義中曹操敗走華容道的故事,相傳在赤壁之戰之后,曹操的船艦被劉備燒了,曹操逃離時面前有兩條路:1、平坦的大路。2、泥濘的華容道。面對這兩條路,曹操沒有選擇大路而選擇有炊煙的小路「華容道路」,理由---實則虛之,虛則實之,那么曹操在選擇道路的時候其實就是選擇策略
敗走華容道的簡單的 UML
根據 UML 編碼
- 1、定義一個路的抽象策略
- 2、定義具體的路徑--大路
- 3、定義具體路線--華容道
- 4、定義上下文,選擇方式
- 5、主角曹操登場,看看曹操是如何選擇道路的
真的走了華容道,好吧 no zuo no die ,我們可以看到上面曹操選擇逃跑路線都是行為,所以很適合策略模式「策略模式就是一種選擇模式,當你舉棋不定的時候就使用策略模式」
注意: 策略的核心不是如何實現算法,而是如何更優雅的把這些算法組織起來,讓客戶端非常好調用「雖然策略非常多,可以自由切換,但是同一時間客戶端只能調用一個策略,其實也很好理解,你不可能同時既坐飛機,又坐火車」。
2、出行旅行方式
經過上面的曹操敗走華榮道,我們對策略有了感覺了吧,那么下來我們趁熱打鐵,再來一發,我們都知道出去旅行一般方式:坐飛機、坐火車、坐大巴、自駕游等等,這一個個的出行方式就是策略,接下來我給出簡單的 UML 圖,代碼部分請各自自行實現「道理都懂,你的生活質量還是沒有提高,方法再多也不見有多成功,就是因為實踐太少,動手才是真理,靠--忘記吃藥了,脈動回來」
出行方式簡單的 UML
代碼實現
大家根據出行的 UML 圖實現代碼即可「非常簡單,相信都可以實現」
3、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'注: 以下代碼純粹是為了演示策略模式,順手寫的,好多細節可能沒有考慮到,但是基本框架就是這樣的,可以自行完善
- 1、分別新建 MVP 的基類接口,IPresenter,Model,IView
- 2、新建 MVP 的關聯接口 ILoginContact.java 「當然也可以不寫此類,直接寫登錄 MVP 的直接子類」
其中 ILoginModel 就是一個抽象策略,這里是登錄功能
- 3、分別實現具體的策略「使用不同的網絡請求庫調用登錄 API」
具體策略1:使用 AsyncHttpClient 調用登錄
/*** @Description 具體策略使用 AsyncHttpClient 來調用登錄 API* @Creator TigerChain(創建者)*/ public class AsynchHppClientImplLogimModel implements ILoginContact.ILoginModel {@Overridepublic Observable<User> login(final String uName, final String uPass) {return Observable.create(new ObservableOnSubscribe<User>() {@Overridepublic void subscribe(final ObservableEmitter<User> e) throws Exception {AsyncHttpClient client = new AsyncHttpClient() ;// 這里就是一個請求 沒有真正的對接服務器,只是一個演示client.get("http://www.baidu.com", new AsyncHttpResponseHandler() {@Overridepublic 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();}}@Overridepublic 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 {@Overridepublic Observable<User> login(final String uName, final String uPass) {return Observable.create(new ObservableOnSubscribe<User>() {@Overridepublic void subscribe(final ObservableEmitter<User> e) throws Exception {/**** 這里調用和 Volley 相關的 API 實現登錄即可*/}});} }具體策略3:使用 RetorFit 調用登錄 API
/*** @Description 具體策略 使用 RetorFit 實現登錄功能性* @Creator TigerChain(創建者)*/ public class RetorFitImplLoginModel implements ILoginContact.ILoginModel {@Overridepublic 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) ; }- 4、策略中的上下文「這里就是我們具體的 P」 LoginPresenterImpl.java
到此為止,我們的 MVP+RX+Retorfit 帶策略的登錄功能就完成了。
- 5、客戶端調用「在 Activity 中調用」
下面來看客戶調用,不貼代碼了「放一張部分代碼截圖」,后面放出全部 DEMO 大家自行查看
怎么樣,通過以上幾個例子,相信我們對策略模式有了很好的理解了
- 6、最后運行看一下
demo 沒有實現完畢,其中 Retorfit 和 Volley 沒有完善,有興趣的可以自行完善
Demo 地址:https://github.com/githubchen001/mvp-rx-loginStrategy
三、Android 源碼中的策略模式
1、TimeInterpolator 時間插值器
做過動畫的朋友都知道,插值器的概念,一句話就是:設置不同的插值器,動畫可以以不同的速度模型來執行
先看看 TimeInterpolator 和它的直接子類
TimeInterpolator 的 UML
從 UML 圖就可以看出 TimeInterpolator 是一個典型的策略模式,你想使用那種插件器,是客戶端的事情,并且結合工廠模式創建各自的插件器
2、ListAdapter
乍一看好像沒有見過這個東東呀,但是我說一個你肯定知道 ListView 知道吧,BaseAdapter「實現了 ListAdapter」 知道吧 ,大家以前肯定都使用過 ListView 「雖然現在推薦使用 RecyclerView ,但是它依然被很多人使用」,它就是一個策略,我們來分析一下
ListAdaper 和它的直接子類
ListAdapter 的簡單的 UML
以上只是 ListAdapter 簡單的一個 UML 圖「問題說明即可,真正的 ListAdapter 比這復雜多」,從上面可以看到 ListAdapter 典型的一個策略模式,有興趣的朋友可以自行跟一下源碼
3、RecyclerView.LayoutManager
RecyclerView.LayoutManager 和它的子類
RecyclerView.LayoutManager 簡單的 UML
可以看到 RecyclerView.LayoutManager 也是一個策略模式
其實不知不覺中我們使用了好多策略模式,只是沒有注意罷了,細細想想,是不是那么回事,再多例子不再舉了。有興趣的朋友可以自已去扒扒 Android 源碼看看那部分使用的是策略模式
四、策略模式和簡單工廠模式
策略模式和簡單工廠非常相似,結構基本上一樣,但是它們側重點不一樣
- 策略模式:是一個行為模式,解決策略的切換和擴展,讓策略獨立于客戶端
- 簡單工廠模式:是一種創建模式「創建對象」,接收指令創建出具體的對象,讓對象的創建和具體的使用客戶無關
但是我們在策略模式中可以使用簡單工廠模式「把生成策略這一過程使用工廠去實現,這樣好不好呢?適合就是最好的」
五、策略模式的優缺點
既然策略模式使用這么廣泛,那么策略模式是不是就是無敵了呢,沒有一點點缺點?肯定不是的。
優點:
- 1、結構清晰,把策略分離成一個個單獨的類「替換了傳統的 if else」
- 2、代碼耦合度降低,安全性提高「各個策略的細節被屏蔽」
缺點:
- 1、客戶端必須要知道所有的策略類,否則你不知道該使用那個策略,所以策略模式適用于提前知道所有策略的情況下
- 2、增加了類的編寫,本來只需要 if else 即可「但是這是所有模式和架構的通病呀」
到此為止我們簡單明了的介紹完了策略模式,最后說一下:點贊是一種美德
總結
以上是生活随笔為你收集整理的人人都会设计模式:03-策略模式--Strategy的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 2017 1
- 下一篇: 【CSWS2014 Summer Sch