MVP + Clean

Clean architecture, some of you may have heard of it. I’m sure a significant number of you have not heard of the Clean architecture.

The most important topic in this article is Clean. MVP will not be mentioned here. If you are interested, click the link below.

From zero to one, take you through the mysteries of MVP and make it happen yourself!

So what is Clean?

concept

Clean, in Chinese, means Clean and tidy. So it can also be called “clear architecture”.

It is a hierarchical architecture approach, combining the presentation layer (implementation layer),

The Data layer (data layer) and domain layer (business logic layer) are independent of each other.

Different layers are connected through interfaces without understanding each other’s implementation.

Why clear? Because it has five characteristics:

  • The framework of independent
  • testability
  • The UI independent
  • Database independence
  • Any external proxy module is independent

Take a picture and feel Clean’s unique charm:

Does it feel square and dazzling? Ha ha ha, it doesn’t matter, we only need to understand the three layers of presentation, domain and data.

structure

  1. presentation

    At this level, you can use MVC, you can use MVP, or you can use MVVM.

    Note that at this level, only the UI-related logic is done. If you use MVP, then every Presenter,

    They are composed of at least one UseCase whose primary task is to perform asynchronous tasks, fetch data,

    And get the data through callbacks for UI rendering.

    Thus, UseCase has largely freed up Presenters.

  2. domain

    Business logic processing layer. Interaction with the outside world, through the interface.

    Simply put, the businesses in the project will be in this layer, one by one will be pulled out.

    Pull it out and give it to who? UseCase, of course. What kind of business does the presentation layer need?

    Just call it through UseCase.

  3. data

    And this layer, which makes sense, is where you get the data.

    There is a Repository for each business, where the data is retrieved.

    The external connection to the data layer is through the Resporitory. The outside world doesn’t need to know where the data is coming from,

    Whether it’s from the local or the network, the external doesn’t need to be concerned.

Demand hypothesis

Users click Button to obtain selected articles in wechat and display them on the interface.

Requirements split:

Get selected wechat articles that can be defined as a business.

GetArticleList (); getArticleList(); getArticleList(); getArticleList();

The Presenter layer receives the command to call usecase.execute (), which is then connected to the domain layer.

In the execute method, data is retrieved from the data layer through the Repository interface.

After obtaining the data, the final presentation layer will get the data through callback and display the list of articles.

Code implementation

Create ArticleView

public interface ArticleView extends IView {

    void getArticleSuccess(List<ArticleBean> articleBeanList);

    void getArticleFail(String failMsg);

}
Copy the code

Create ArticlePresenter

public class ArticlePresenter extends BasePresenter<ArticleView> {

    public void getArticleList(String key) {
        
    }

}
Copy the code

As we said above, the Presenter layer is made up of one or more USecases,

Their main task is to perform asynchronous tasks and retrieve data. So let’s define a UseCase at the domain layer,

Called ArticleCase, the detailed code is as follows:

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) {
        returnarticleRepository.getArticleList(s); }}Copy the code

UseCase is a wrapped Case base class:

@param <T> return data @param <Params> request parameter */ public class UseCase<T, Params> { private final CompositeDisposable mDisposables; publicUseCase() {
        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(); }}}Copy the code

Any business class needs to inherit UseCase and implement the buildObservable method.

So ArticleCase, we’re using the articlepository interface, and obviously,

This interface is implemented by the Data layer to retrieve data and call back.

public interface ArticleRepository {

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

}
Copy the code

Next in the data layer, implement 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 {
                        returnbaseResponse.getResult().getList(); }}); }}Copy the code

At this point, you get the data. Whether obtained from the network or locally, the Domain layer does not need to know.

Then implement ArticleCase in the Presenter layer and call the execute() method to get the data.

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);
        returnmCaseList; }}Copy the code

And implement automatic unsubscribe in 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 voiddetach() {
        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;
    }

}
Copy the code

Does it feel round? It doesn’t matter, combine simple business, repeat the code a few times, you will understand, trust me.

See demo for specific code implementation.

conclusion

Through the above code, to achieve a business, although wrote a lot of classes, a lot of code.

But the benefits are obvious: the overall architecture is clearer, easier to maintain, easier to test, more cohesive, and less coupled.

I also hope that those of you who have read this article can do it yourself. Through practice, you will understand a lot of questions that you didn’t understand before.

Hope you found this article useful. Thank you.

Finally, attach the demo address: MVP-clean-demo

Your star is my motivation, welcome star!