带你动手实现 MVP+Clean架构!

MVP + Clean

Clean 架构,有的同窗可能有所耳闻。确定也有至关一部分同窗没据说过 Clean 架构。git

本篇文章重要讲解的是 Clean,MVP 在这里就再也不赘述,感兴趣的戳下方连接。github

从 0 到 1,带你解剖 MVP 的神秘之处,并本身动手实现 MVP !数据库

那么先来解释一下,何为 Clean?api

概念

Clean,中文意思为清洁的、整齐的。因此也能够称其为 "清晰架构"bash

它是一种分层架构方式,将 presentation 层(实现层)、微信

data 层(数据层)以及domain 层(业务逻辑层),彼此独立。网络

不一样层之间,经过接口来链接,却又不了解彼此的具体实现。架构

为何清晰?由于它有五大特性:app

  • 框架独立
  • 可测试性
  • UI 独立
  • 数据库独立
  • 任何外部代理模块独立

放一张图片,感觉一下,Clean 的独特魅力: 框架

这里写图片描述

是否是感受很方很晃眼?哈哈哈,不要紧,咱们只须要理解 presentation、domain、data 三层就能够。

结构

  1. presentation

    在这一层,你可使用 MVC,也可使用 MVP,或者 MVVM。

    注意,在这一层,只作与 UI 相关的逻辑。若是你使用 MVP,那么,每个 Presenter,

    都至少会有一个 UseCase 组成,他们的主要任务是执行异步任务,获取数据,

    并经过回调拿到数据,供 UI 渲染。

    因而可知,UseCase 的出现,很大程度上释放了 Presenter。

  2. domain

    业务逻辑处理层。与外部的交互,经过接口实现。

    简单理解,项目中的业务会在这一层中,一个一个的会被抽离出来。

    抽离出来,交给谁处理呢?固然是 UseCase,presentation 层须要哪一种业务,

    就经过 UseCase 调用就能够了。

  3. data

    这一层,也很好理解,就是获取数据的地方。

    每一个业务会对应一个 Repository,具体获取数据的操做就在这里。

    外部与 data 层链接,是经过 Resporitory。外部不须要知道数据是从哪里获取,

    不论是从本地,仍是从网络,外部也不须要关心。

需求假设

用户点击 Button 按钮,获取微信精选文章,在界面显示。

需求拆分:

获取微信精选文章,能够定义为一个业务。

在 presentation 层,点击了 Button 按钮,向 Presenter 层,发出获取文章指令 getArticleList()

Presenter 层收到指令,调用 UseCase.execute(),这时就与 domain 层产生联系了。

在 execute 方法中,经过 Repository 接口从 data 层获取数据。

获取数据后,再经过回调,最终 presentation 层拿到数据,进行文章列表展现。

这里写图片描述

代码实现

建立 ArticleView

public interface ArticleView extends IView {

    void getArticleSuccess(List<ArticleBean> articleBeanList);

    void getArticleFail(String failMsg);

}
复制代码

建立 ArticlePresenter

public class ArticlePresenter extends BasePresenter<ArticleView> {

    public void getArticleList(String key) {
        
    }

}
复制代码

上面咱们说过,Presenter 层是由一个或多个 UseCase 组成,

他们的主要任务是执行异步任务,获取数据。那么咱们在 domain 层定义一个 UseCase

名为 ArticleCase,详细代码以下:

public class ArticleCase extends UseCase<List<ArticleBean>, String> {

    private ArticleRepository articleRepository;

    public ArticleCase(ArticleRepository repository) {
        this.articleRepository= repository;
    }

    @Override
    protected Observable<List<ArticleBean>> buildObservable(String s) {
        return articleRepository.getArticleList(s);
    }

}
复制代码

UseCase 是封装好的一个 Case 基类:

/**
 * Case 基类
 *
 * @param <T>      返回数据
 * @param <Params> 请求参数
 */
public abstract class UseCase<T, Params> {

    private final CompositeDisposable mDisposables;

    public UseCase() {
        this.mDisposables = new CompositeDisposable();
    }

    @SuppressLint("CheckResult")
    public void execute(Params params, Consumer nextConsumer, Consumer errorConsumer) {
        Observable<T> observable = this.buildObservable(params);
        addDisposable(observable.subscribe(nextConsumer, errorConsumer));
    }

    protected abstract Observable<T> buildObservable(Params params);

    private void addDisposable(Disposable disposable) {
        mDisposables.add(disposable);
    }

    @SuppressLint("CheckResult")
    public void execute(Params params, BaseObserver<T> observer) {
        Observable<T> observable = this.buildObservable(params);
        observable.subscribe(observer);
        addDisposable(observer.getDisposable());
    }

    public void dispose() {
        if (!mDisposables.isDisposed()) {
            mDisposables.dispose();
        }
    }

}
复制代码

任何一个业务类,都须要去继承 UseCase,并实现 buildObservable 方法。

继续看 ArticleCase,咱们用到了接口 ArticleRepository,很明显,

这个接口用于被 data 层实现,从而获取数据并回调。

public interface ArticleRepository {

    Observable<List<ArticleBean>> getArticleList(String param);

}
复制代码

接下来在 data 层,去实现 ArticleRepository

public class ArticleRepositoryImpl implements ArticleRepository {

    private DataApi mApi;

    public ArticleRepositoryImpl() {
        mApi = JDHttp.createApi(DataApi.class);
    }

    @Override
    public Observable<List<ArticleBean>> getArticleList(String param) {
        return mApi.getArticleList(param).compose(JDTransformer.<BaseResponse>switchSchedulers())
                .map(new Function<BaseResponse, List<ArticleBean>>() {
                    @Override
                    public List<ArticleBean> apply(BaseResponse baseResponse) throws Exception {
                        return baseResponse.getResult().getList();
                    }
                });
    }
}
复制代码

在这里,进行了获取数据操做。不管是从网络、仍是本地获取,domain 层不须要知道。

而后在 Presenter 层中实现 ArticleCase,并调用 execute()方法,获取数据。

public class ArticlePresenter extends BasePresenter<ArticleView> {

    private ArticleCase mCase;

    public void getData(String key) {
        mCase.execute(key, new BaseObserver<List<ArticleBean>>() {
            @Override
            public void onSuccess(List<ArticleBean> articleBeanList) {
                getView().getArticleSuccess(articleBeanList);
            }

            @Override
            public void onFail(String failMsg) {
                getView().getArticleFail(failMsg);
            }
        });
    }

    @Override
    public List<UseCase> createCases() {
        mCase = new ArticleCase(new ArticleRepositoryImpl());
        mCaseList.add(mCase);
        return mCaseList;
    }
}
复制代码

而且在 BasePresenter 中实现自动取消订阅:

public abstract class BasePresenter<V extends IView> implements IPresenter<V> {

    private V mView;

    private CPBridge mBridge = new CPBridge();
    
    protected List<UseCase> mCaseList = new ArrayList<>();

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void attach(V view) {
        this.mView = view;
        createCases();
        mCaseList.forEach(new Consumer<UseCase>() {
            @Override
            public void accept(UseCase useCase) {
                mBridge.addCase(useCase);
            }
        });
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void detach() {
        this.mView = null;
        mBridge.removeCase();
    }

    protected abstract List<UseCase> createCases();

    public V getView() {
        if (isViewAttached()) {
            return mView;
        } else {
            throw new IllegalStateException("Please call IPresenter.attach(IView view) before requesting data");
        }
    }

    private boolean isViewAttached() {
        return null != mView;
    }

}
复制代码

感受绕吗?不要紧,结合简单的业务,将代码反复的敲几回,你就明白了,相信我。

具体代码实现请看 demo。

总结

经过上面的代码,实现一个业务,虽然写了好多类,好多代码。

可是这样写的好处也是显而易见的:总体架构更加清晰、易维护、方便测试、高内聚、低耦合。

同时也但愿阅读过这篇文章的人,能够亲手实践。经过实践,你会明白不少以前不明白的问题。

但愿这篇文章能对你有用,谢谢。

最后,附上 demo 地址:MVP-Clean-Demo

您的 star 是我前进的动力,欢迎 star!

相关文章
相关标签/搜索