mvp优化你的代码

MVP是什么?

Most Valuable Player(最有价值运动球员)? 不不不,虽然我很喜欢看nba,但此MVP非彼MVP。
这里的MVP是指安卓中的一种开发模式。
它将代码整体分为M(Model)、V(View)、P(Presenter)三层。

正经版:
M层(model):数据模型/处理层。负责数据处理、数据提供,如网络请求,数据库操作
V层(view):视图展示层。负责界面展示,如Activity,Fragment
P层(presenter):业务逻辑层。负责业务逻辑服务,是V层与M层间的桥梁

640?wxfrom=5&wx_lazy=1MVP示意图1

你也可以这样帮助理解下(餐厅版):
M层(model):厨师。负责做菜
V层(view):顾客。点餐吃饭
P层(presenter):服务员。提供下单、上菜等各种服务

640MVP示意图2

与MVC模式的区别:
MVC中,V层与M层是可以互通的,而在MVP中V层与M层是不通的。
按餐厅版来说就是,MVC中顾客可以直接告诉厨师要吃什么菜,厨师做好后直接把菜端到你面前,而MVP中只能通过服务员来完成点餐到用餐的过程。

使用MVP有什么好处?

抽象些来说:
MVP可以降低代码耦合度,提高代码的结构清晰度、可读性、维护性和复用性。

具体些来说(参考JessYan的例子):
现在有这么一个需求:Activity中从网络获取数据然后展示在A控件上。
如果不用MVP的话,那就直接把获取展示等代码都写在Activity中,很快便可以写完。

但现在需求变动了:
1.要求加入缓存功能,如果本地有数据,则先从本地获取数据,然后再从网络获取最新数据进行替换
2.要求数据展示在B控件上而不是A控件。

如果代码都是你自己写的,那改起来还比较轻松,但假如是团队开发,代码不是你写的,你需要花时间把逻辑重新看一遍再开始改,而且如果改错的话,会影响之前已经写好的功能。

但使用MVP模式进行开发就不同了。由于它的分工结构清晰,V层仅负责数据展示,P层仅负责业务逻辑,M层仅负责数据获取/处理。所以改动起来就轻松很多。
对于变动的需求1:我们只需在P层加入逻辑判断(先从本地获取,再网络获取),然后M层增加一个从本地获取数据方法。
对于变动的需求2:我们只需在V层修改获取到数据后的展示方式,从控件A改成控件B。

扫描二维码关注公众号,回复: 45147 查看本文章

当然还有可复用等优点,这里就不具体讲了。
至于"缺点"嘛,就是会相应地增加代码量。有得有失,但得大于失。

具体使用

以这么一个场景为例:
从网络获取“正在上映”的电影数据,获取成功则将数据在页面展示,获取失败则给出相应提示。

需要写四个部分:Model层,View层,Presenter层,接口

接口
负责“连接”MVP三层,以便方法调用、数据流动。同时也便于进行单元测试。

IView
View层接口,定义View层需实现的方法,P层通过该接口回调通知View层。

public interface IMovieView {
    //成功获取到电影数据
    void getMovieSuccess(List<MovieRes> list, int type);
    //获取电影数据失败
    void getMovieFail(int status, String desc, int type);
}

IModel
Model层接口,定义Model层需实现的方法,P层通过该接口调用M层获取/处理数据的方法。

public interface IMovieMoel{
    //请求正在上映的电影数据
    Observable getPlayingMovie(int start, int count);
    ...
}

Model层
实现IModel接口中的方法,负责数据的获取/处理。

public class MovieModel implements IMovieMoel{

    @Override
    public Observable getPlayingMovie(int start,int count) {
        //提供数据源
        return DevRing.httpManager().getService(MovieApiService.class).getPlayingMovie(start, count);
    }
    ...
}

Presenter层
处理业务逻辑,调用M层获取数据,调用V层传递展示数据。

public class MoviePresenter {
    private IMovieView mIView;
    private IMovieModel mIModel;

    public MoviePresenter(IMovieView iMovieView, IMovieMoel iMovieMoel) {
        mIView = iMovieView;
        mIModel = iMovieModel;
    }

    /**
     * 获取正在上映的电影
     *
     * @param start 请求电影的起始位置
     * @param count 获取的电影数量
     * @param type  类型:初始化数据INIT、刷新数据REFRESH、加载更多数据LOADMORE
     */
    public void getPlayingMovie(int start, int count, final int type) {

        DevRing.httpManager().commonRequest( mIModel.getPlayingMovie(start, count),
         new CommonObserver<HttpResult<List<MovieRes>>>() {
            @Override
            public void onResult(HttpResult<List<MovieRes>> result) {
                if (mIView != null) {
                    mIView.getMovieSuccess(result.getSubjects(), type);
                }
            }

            @Override
            public void onError(int errType, String errMessage) {
                if (mIView != null) {
                    mIView.getMovieFail(errType, errMessage, type);
                }
            }
        }, RxLifecycleUtil.bindUntilDestroy(mIView));
    }

    ...

     /**
     * 释放引用,防止内存泄露
     */
    public void destroy() {
        mIView = null;
    }
}

View层
实现IView接口中的方法,对获取到的数据进行展示

public class MovieFragment implements IMovieView {
    //获取电影数据成功的网络请求回调
    @Override
    public void getMovieSuccess(List<MovieRes> list, int type) {
        //成功,对数据进行展示
        ....
    }

    //获取电影数据失败的网络请求回调
    @Override
    public void getMovieFail(int status, String desc, int type) {
        //失败,界面上做出相应提示
        ...
    }
}

完成以上几步后,在View层初始化时,调用Presenter层方法即可。

@Override
protected void initData() {
      mPresenter = new MoviePresenter(this, new MovieModel());
      mPresenter.getPlayingMovie(start, mCount, type);
}

还有一点需注意:
如果Presenter层持有了View层的引用,那么记得在V层销毁时,把Presenter层中对View层的引用置null,避免View层回收失败导致内存泄漏。

@Override
public void onDestroy() {
     super.onDestroy();
     if (mPresenter != null) {
          mPresenter.destroy();
          mPresenter = null;
     }

猜你喜欢

转载自blog.csdn.net/weixin_41923014/article/details/80027613
MVP
今日推荐