版權(quán)聲明:本文為博主原創(chuàng)文章款筑,未經(jīng)博主允許不得轉(zhuǎn)載
PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.reibang.com/p/135532803cdb
本文出自 TigerChain 簡(jiǎn)書 人人都會(huì)設(shè)計(jì)模式
教程簡(jiǎn)介
- 1腋寨、閱讀對(duì)象
本篇教程適合新手閱讀粉寞,老手直接略過(guò) - 2、教程難度
初級(jí)烛亦,本人水平有限诈泼,文章內(nèi)容難免會(huì)出現(xiàn)問題,如果有問題歡迎指出煤禽,謝謝 - 3铐达、Demo 地址
https://github.com/tigerchain/DesignPattern 請(qǐng)看 Strategy 部分
正文
一、什么是策略模式
1檬果、 生活中的策略
比如說(shuō)我要出行旅游瓮孙,那么出行方式有--飛機(jī)、自駕游选脊、火車等杭抠,這幾種方式就是策略。再比如:某大型商場(chǎng)搞活動(dòng)--滿 100 元送杯子恳啥,滿 300 減 50 偏灿,滿 1000 元抽獎(jiǎng)「一等將彩色電視機(jī)」,這種活動(dòng)也是策略钝的。在游戲中翁垂,我們打一個(gè)普通的怪使用普通的招即可,打大 BOSS 就要是用大招硝桩,這也是一種策略 ...
2沿猜、程序中的策略
就是對(duì)各個(gè)算法的一個(gè)封裝「不是實(shí)現(xiàn)算法,而是封裝算法」亿柑,讓客戶端非常容易的調(diào)用邢疙,省掉了客戶端 if else 惡心的判斷,讓客戶端獨(dú)立于各個(gè)策略
這里舉一個(gè)簡(jiǎn)單的例子:比如我們?cè)?Android 中一定會(huì)使用到 http 網(wǎng)絡(luò)請(qǐng)求,請(qǐng)求庫(kù)太多了疟游,大概有 AsyncHttpclient,OkhttpClient,Volley 等「具體的策略」呼畸,那么我們完全可以使用一個(gè)策略模式,定義一個(gè)抽像策略颁虐,然后把各個(gè)請(qǐng)求策略封裝蛮原,客戶想使用哪個(gè)就使用哪個(gè),非常靈活和方便
策略模式和簡(jiǎn)單工廠很相似另绩,確有不同儒陨,策略是一種行為模式,而簡(jiǎn)單工廠是創(chuàng)建型模式「創(chuàng)建對(duì)象」 后面再說(shuō)
策略模式的定義
策略是對(duì)算法的封裝笋籽,是一種形為模式蹦漠,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中,從而使得它們可以相互替換
策略的特點(diǎn)
- 是一種行為模式车海,對(duì)算法封裝笛园,使得客戶端獨(dú)立于各個(gè)策略
- 擴(kuò)展性強(qiáng),添加策略無(wú)非就是添加一個(gè)具體的實(shí)現(xiàn)類而已侍芝,代價(jià)非常低
策略模式的結(jié)構(gòu)
角色 | 類別 | 說(shuō)明 |
---|---|---|
Strategy | 抽象的策略 | 是一個(gè)接口或抽象類 |
ConcreteStrategy | 具體的策略類 | 實(shí)現(xiàn)了抽象的策略 |
Context | 一個(gè)普通的類 | 上下文環(huán)境研铆,持有 Stragegy 的引用 |
策略模式簡(jiǎn)單的 UML
二、策略模式舉例
1州叠、曹操敗走華榮道
我們知道三國(guó)演義中曹操敗走華容道的故事棵红,相傳在赤壁之戰(zhàn)之后,曹操的船艦被劉備燒了咧栗,曹操逃離時(shí)面前有兩條路:1逆甜、平坦的大路。2楼熄、泥濘的華容道忆绰。面對(duì)這兩條路浩峡,曹操?zèng)]有選擇大路而選擇有炊煙的小路「華容道路」可岂,理由---實(shí)則虛之,虛則實(shí)之翰灾,那么曹操在選擇道路的時(shí)候其實(shí)就是選擇策略
敗走華容道的簡(jiǎn)單的 UML
根據(jù) UML 編碼
- 1缕粹、定義一個(gè)路的抽象策略
/**
* 抽象的策略,定義逃跑路線
*/
public interface IRunStrategy {
// 逃跑線路
void escapeRoute() ;
}
- 2纸淮、定義具體的路徑--大路
/**
* 具體的策略一走大路
*/
public class Highroad implements IRunStrategy {
@Override
public void escapeRoute() {
System.out.println("走大路");
}
}
- 3平斩、定義具體路線--華容道
/**
* 具體的策略二走華容道
*/
public class HuaRongRoad implements IRunStrategy {
@Override
public void escapeRoute() {
System.out.println("走華容道");
}
}
- 4、定義上下文咽块,選擇方式
/**
* 上下文 持有 IRunStrategy 的引用
*/
public class ContextRunStrategy {
private IRunStrategy iRunStrategy ;
public ContextRunStrategy(IRunStrategy iRunStrategy){
this.iRunStrategy = iRunStrategy ;
}
/**
* 選擇道路
*/
public void choiceRoad(){
iRunStrategy.escapeRoute();
}
}
- 5绘面、主角曹操登場(chǎng),看看曹操是如何選擇道路的
/**
* 曹操選擇路線
*/
public class CaoCao {
public static void main(String args[]){
/**
* 曹操疑心重,選擇了華容道揭璃,對(duì)曹操來(lái)說(shuō)至于雜樣走華容道晚凿,不關(guān)心,死多少人也不關(guān)心瘦馍,只關(guān)心我要走這條道就好
*/
IRunStrategy huaRongRoad = new HuaRongRoad() ;
ContextRunStrategy contextRunStrategy = new ContextRunStrategy(huaRongRoad) ;
contextRunStrategy.choiceRoad();
}
}
真的走了華容道歼秽,好吧 no zuo no die ,我們可以看到上面曹操選擇逃跑路線都是行為情组,所以很適合策略模式「策略模式就是一種選擇模式燥筷,當(dāng)你舉棋不定的時(shí)候就使用策略模式」
注意: 策略的核心不是如何實(shí)現(xiàn)算法,而是如何更優(yōu)雅的把這些算法組織起來(lái)院崇,讓客戶端非常好調(diào)用「雖然策略非常多肆氓,可以自由切換,但是同一時(shí)間客戶端只能調(diào)用一個(gè)策略底瓣,其實(shí)也很好理解做院,你不可能同時(shí)既坐飛機(jī),又坐火車」濒持。
2键耕、出行旅行方式
經(jīng)過(guò)上面的曹操敗走華榮道,我們對(duì)策略有了感覺了吧柑营,那么下來(lái)我們趁熱打鐵屈雄,再來(lái)一發(fā),我們都知道出去旅行一般方式:坐飛機(jī)官套、坐火車酒奶、坐大巴、自駕游等等奶赔,這一個(gè)個(gè)的出行方式就是策略惋嚎,接下來(lái)我給出簡(jiǎn)單的 UML 圖,代碼部分請(qǐng)各自自行實(shí)現(xiàn)「道理都懂站刑,你的生活質(zhì)量還是沒有提高另伍,方法再多也不見有多成功,就是因?yàn)閷?shí)踐太少绞旅,動(dòng)手才是真理摆尝,靠--忘記吃藥了,脈動(dòng)回來(lái)」
出行方式簡(jiǎn)單的 UML
代碼實(shí)現(xiàn)
大家根據(jù)出行的 UML 圖實(shí)現(xiàn)代碼即可「非常簡(jiǎn)單因悲,相信都可以實(shí)現(xiàn)」
3堕汞、Android 中使用策略場(chǎng)景
段子來(lái)了
先看個(gè)段子,輕松一下「注以下只是一個(gè)簡(jiǎn)單舉例晃琳,庫(kù)不分先后讯检,俗話說(shuō)沒有最好琐鲁,只有最適合」
相信做 Android 的朋友都離不開網(wǎng)絡(luò)請(qǐng)求,有一天你「小明」發(fā)現(xiàn)了一個(gè)傳說(shuō)中很好的網(wǎng)絡(luò)請(qǐng)求庫(kù) AsyncHttpClient 人灼,你高興的把網(wǎng)絡(luò)請(qǐng)求相關(guān)的 API 都實(shí)現(xiàn)了绣否,經(jīng)理看到了說(shuō)不錯(cuò)不錯(cuò),寫的很快嗎挡毅,突然有一天蒜撮,經(jīng)理說(shuō):小明 AsyncHttpClient 好多 API 過(guò)時(shí)了「隨著 Android 版本的更新」,并且對(duì) RestFul 支持的不太友好跪呈,我看到一個(gè)叫 Retorfit2「聽說(shuō)是最好的網(wǎng)絡(luò)」 的庫(kù)段磨,默認(rèn)支持 OkHttp ,用 Retorfit 把 AsyncHttpClient 替換了吧耗绿,非常簡(jiǎn)單對(duì)你來(lái)說(shuō)苹支,小明這時(shí)估計(jì)心里飄過(guò)了一千匹羊駝「我靠,為麻不早說(shuō)」误阻,又過(guò)了一些時(shí)間债蜜,經(jīng)理又說(shuō),小明呀究反,Volley 是 Google 推薦的網(wǎng)絡(luò)請(qǐng)求庫(kù)寻定,你換成 Volley 庫(kù)吧,小明此時(shí)估計(jì)把經(jīng)理的八輩祖宗都問候了一遍精耐,又是一通加班加點(diǎn)的改狼速,最后 Happy 的改好了。后面又有一個(gè)牛 B 的庫(kù)卦停,經(jīng)理又讓替換向胡,小明哭了「為什么受傷的總是我」...
看到這里大家應(yīng)該想到了,上面的請(qǐng)求場(chǎng)景就是一個(gè)個(gè)的策略惊完,如果小明按照策略模式走下來(lái)僵芹,只是添加擴(kuò)展子策略,壓根原來(lái)的方法毛都不用改小槐,只能說(shuō)拇派,小明呀,你可張點(diǎn)心吧本股。
MVP + 策略模式
下面我們使用 MVP + 策略模式模擬一個(gè)簡(jiǎn)單的登錄功能攀痊,實(shí)現(xiàn)上面小明的需求
MVP+retorfit+rx 請(qǐng)求策略簡(jiǎn)單的 UML
根據(jù) UML 擼碼
首先我們要使用 AsyncHttpClient桐腌、Retorfit 等拄显,先添加配置 Gradle「項(xiàng)目 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'
注: 以下代碼純粹是為了演示策略模式,順手寫的案站,好多細(xì)節(jié)可能沒有考慮到躬审,但是基本框架就是這樣的,可以自行完善
- 1、分別新建 MVP 的基類接口,IPresenter,Model,IView
/**
* @Description MVP 中的 Presenter 基
* @Creator TigerChain(創(chuàng)建者)
*/
public interface Presenter {
}
/**
* @Description MVP 中的 Model 基類
* @Creator TigerChain(創(chuàng)建者)
*/
public interface Model {
}
/**
* @Description MVP 中的 View 基類
* @Creator TigerChain(創(chuàng)建者)
*/
public interface IView {
}
- 2承边、新建 MVP 的關(guān)聯(lián)接口
ILoginContact.java
「當(dāng)然也可以不寫此類遭殉,直接寫登錄 MVP 的直接子類」
package designpattern.tigerchain.com.mvphttpstrategy.mvp;
import designpattern.tigerchain.com.mvphttpstrategy.mvp.domain.User;
import io.reactivex.Observable;
/**
* @Description MVP 的關(guān)聯(lián)類「也可以單獨(dú)創(chuàng)建 MVP 就是有點(diǎn)亂」
* @Creator TigerChain(創(chuàng)建者)
*/
public interface ILoginContact {
interface LoginView extends IView{
//顯示進(jìn)度條
void showProgress() ;
//隱藏進(jìn)度條
void hideProgress() ;
//登錄成功
void loadSuccess(String str) ;
//登錄失敗
void loadFailed(String str) ;
//取得用戶名
String getUserName() ;
//取得用戶密碼
String getUserPass() ;
//清除輸入框
void clearEditText() ;
//用戶名和密碼不能為空
void editnotNull() ;
}
interface LoginPresenter extends Presenter{類
/**
* 登錄功能
*/
void login() ;
/**
* 清除輸入框架內(nèi)容
*/
void clear() ;
}
interface ILoginModel extends Model{
/***
* 登錄的方法,其實(shí)這里就是一個(gè)抽象策略博助,至于你使用 retrofit 還是 asynchttpClient 還是 Volley 那是自己的事情
* @param uName
* @param uPass
* @return
*/
Observable<User> login(String uName, String uPass) ;
}
}
其中 ILoginModel 就是一個(gè)抽象策略险污,這里是登錄功能
- 3、分別實(shí)現(xiàn)具體的策略「使用不同的網(wǎng)絡(luò)請(qǐng)求庫(kù)調(diào)用登錄 API」
具體策略1:使用 AsyncHttpClient 調(diào)用登錄
/**
* @Description 具體策略使用 AsyncHttpClient 來(lái)調(diào)用登錄 API
* @Creator TigerChain(創(chuàng)建者)
*/
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() ;
// 這里就是一個(gè)請(qǐng)求 沒有真正的對(duì)接服務(wù)器富岳,只是一個(gè)演示
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 調(diào)用登錄 API
/**
* @Description 具體策略使用 Volley 實(shí)現(xiàn)登錄功能
* @Creator TigerChain(創(chuàng)建者)
*/
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 {
/***
* 這里調(diào)用和 Volley 相關(guān)的 API 實(shí)現(xiàn)登錄即可
*/
}
});
}
}
具體策略3:使用 RetorFit 調(diào)用登錄 API
/**
* @Description 具體策略 使用 RetorFit 實(shí)現(xiàn)登錄功能性
* @Creator TigerChain(創(chuàng)建者)
*/
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(創(chuàng)建者)
*/
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(創(chuàng)建者)
*/
public interface ILoginRetorfitApi {
@GET("/login")
Observable<User> login( @Field("userName") String userName,
@Field("passWord")String passWord) ;
}
- 4蛔糯、策略中的上下文「這里就是我們具體的 P」 LoginPresenterImpl.java
/**
* @Description MVP 中的P ,就相當(dāng)于策略中Context
* @Creator junjun(創(chuàng)建者)
*/
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 本身就是在子線程去請(qǐng)求的窖式,所以這里為了演示把這個(gè)去掉
.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("用戶名或密碼錯(cuò)誤蚁飒,登錄失敗");
loginView.hideProgress();
}
@Override
public void onComplete() {
loginView.hideProgress();
}
}) ;
}
@Override
public void clear() {
loginView.clearEditText();
}
}
到此為止,我們的 MVP+RX+Retorfit 帶策略的登錄功能就完成了萝喘。
- 5淮逻、客戶端調(diào)用「在 Activity 中調(diào)用」
下面來(lái)看客戶調(diào)用,不貼代碼了「放一張部分代碼截圖」阁簸,后面放出全部 DEMO 大家自行查看
怎么樣爬早,通過(guò)以上幾個(gè)例子,相信我們對(duì)策略模式有了很好的理解了
- 6启妹、最后運(yùn)行看一下
demo 沒有實(shí)現(xiàn)完畢凸椿,其中 Retorfit 和 Volley 沒有完善,有興趣的可以自行完善
三翅溺、Android 源碼中的策略模式
1脑漫、TimeInterpolator 時(shí)間插值器
做過(guò)動(dòng)畫的朋友都知道,插值器的概念咙崎,一句話就是:設(shè)置不同的插值器优幸,動(dòng)畫可以以不同的速度模型來(lái)執(zhí)行
先看看 TimeInterpolator 和它的直接子類
TimeInterpolator 的 UML
從 UML 圖就可以看出 TimeInterpolator 是一個(gè)典型的策略模式,你想使用那種插件器褪猛,是客戶端的事情网杆,并且結(jié)合工廠模式創(chuàng)建各自的插件器
2、ListAdapter
乍一看好像沒有見過(guò)這個(gè)東東呀伊滋,但是我說(shuō)一個(gè)你肯定知道 ListView 知道吧碳却,BaseAdapter「實(shí)現(xiàn)了 ListAdapter」 知道吧 ,大家以前肯定都使用過(guò) ListView 「雖然現(xiàn)在推薦使用 RecyclerView 笑旺,但是它依然被很多人使用」,它就是一個(gè)策略昼浦,我們來(lái)分析一下
ListAdaper 和它的直接子類
ListAdapter 的簡(jiǎn)單的 UML
以上只是 ListAdapter 簡(jiǎn)單的一個(gè) UML 圖「問題說(shuō)明即可,真正的 ListAdapter 比這復(fù)雜多」筒主,從上面可以看到 ListAdapter 典型的一個(gè)策略模式关噪,有興趣的朋友可以自行跟一下源碼
3鸟蟹、RecyclerView.LayoutManager
RecyclerView.LayoutManager 和它的子類
RecyclerView.LayoutManager 簡(jiǎn)單的 UML
可以看到 RecyclerView.LayoutManager 也是一個(gè)策略模式
其實(shí)不知不覺中我們使用了好多策略模式,只是沒有注意罷了使兔,細(xì)細(xì)想想建钥,是不是那么回事,再多例子不再舉了虐沥。有興趣的朋友可以自已去扒扒 Android 源碼看看那部分使用的是策略模式
四熊经、策略模式和簡(jiǎn)單工廠模式
策略模式和簡(jiǎn)單工廠非常相似,結(jié)構(gòu)基本上一樣欲险,但是它們側(cè)重點(diǎn)不一樣
- 策略模式:是一個(gè)行為模式奈搜,解決策略的切換和擴(kuò)展,讓策略獨(dú)立于客戶端
- 簡(jiǎn)單工廠模式:是一種創(chuàng)建模式「創(chuàng)建對(duì)象」盯荤,接收指令創(chuàng)建出具體的對(duì)象馋吗,讓對(duì)象的創(chuàng)建和具體的使用客戶無(wú)關(guān)
但是我們?cè)诓呗阅J街锌梢允褂煤?jiǎn)單工廠模式「把生成策略這一過(guò)程使用工廠去實(shí)現(xiàn),這樣好不好呢秋秤?適合就是最好的」
五宏粤、策略模式的優(yōu)缺點(diǎn)
既然策略模式使用這么廣泛父腕,那么策略模式是不是就是無(wú)敵了呢丢胚,沒有一點(diǎn)點(diǎn)缺點(diǎn)?肯定不是的寒波。
優(yōu)點(diǎn):
- 1鞋真、結(jié)構(gòu)清晰崇堰,把策略分離成一個(gè)個(gè)單獨(dú)的類「替換了傳統(tǒng)的 if else」
- 2、代碼耦合度降低涩咖,安全性提高「各個(gè)策略的細(xì)節(jié)被屏蔽」
缺點(diǎn):
- 1海诲、客戶端必須要知道所有的策略類,否則你不知道該使用那個(gè)策略檩互,所以策略模式適用于提前知道所有策略的情況下
- 2特幔、增加了類的編寫,本來(lái)只需要 if else 即可「但是這是所有模式和架構(gòu)的通病呀」
到此為止我們簡(jiǎn)單明了的介紹完了策略模式闸昨,最后說(shuō)一下:點(diǎn)贊是一種美德