人人都会设计模式---策略模式--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 便可「可是这是全部模式和架构的通病呀」

到此为止咱们简单明了的介绍完了策略模式,最后说一下:点赞是一种美德

相关文章
相关标签/搜索