干净的架构,用例和实体

w00*_*0ly 1 architecture android clean-architecture

好的,所以我刚刚开始一个新的Android项目,并想尝试实现Bob叔叔的Clean Architecture。我开始使用RxJava以及GitHub示例和样板以及Fernando Cerjas的博客(如本文)中的内容开始就很不错,但是对于如何实现一些UseCases还是有一些疑问。


TL; DR

一个实体是否应该具有另一个实体的字段(在我的示例中,User具有一个List<Messages>字段)?

还是Presenter应该结合UseCases来构建映射到多个实体的ViewModel(然后如何编码映射器?)?

或者Presenter应该具有与每个UseCase / Entity关联的ViewModel,并创建某种“等待所有数据到onNext”以为每个ViewModel调用view.show()?


基本上,UseCases应该只返回实体吗?实体是否可以由其他实体组成(例如在类的字段中)?实体仅是愚蠢的数据模型POJO吗?您如何表示“联接SQL”查询?

例如,让我们以一个简单的用户/消息应用程序为例。我要实现两个视图:UserList和UserDetails:

  • UserList 显示列表 Users
  • UserDetails 显示用户的信息及其最新消息。

UserList 非常简单,我可以看到如何编写相关的UseCase和图层(下面的代码)。

我的问题是UserDetails屏幕。

GetUserInfoUseCase如果我想同时在视图中传递所有数据,应该如何编码(例如,构建一个由User类组成的ViewModel和一个字段List)?的返回值应该是GetUserInfoUseCase多少?我应该编码a Observable<User> GetUserInfoUseCase和a Observable<List<Message>> GetUserLatestMessages并以某种方式将其合并到演示者中吗?如果是,由于我的Presenter中没有Observable(我只传递一个Observer作为UseCases参数),我该如何管理呢?

用户实体

public abstract class User {
    public abstract long id();
    public abstract String name();
 ...
}
Run Code Online (Sandbox Code Playgroud)

消息实体

public abstract class Message {
    public abstract long id();
    public abstract long senderId();
    public abstract String text();
    public abstract long timstamp();
 ...
}
Run Code Online (Sandbox Code Playgroud)

GetUsersUseCase

public class GetUsersUseCase extends UseCaseObservableWithParameter<Boolean, List<User>, UsersRepository> {

@Inject
public GetUsersUseCase(UsersRepository UsersRepository,
                              @Named("Thread") Scheduler threadScheduler,
                              @Named("PostExecution") Scheduler postExecutionScheduler) {
    super(usersRepository, threadScheduler, postExecutionScheduler);
}

@Override
protected Observable<List<User>> buildObservable(Boolean forceRefresh) {

    if(forceRefresh)
        repository.invalidateCache();

    return repository.getUsers();
}
}
Run Code Online (Sandbox Code Playgroud)

用户演示者

public class UsersPresenter extends BasePresenter<UsersContract.View> implements UsersContract.Presenter {

    @Inject
    GetUsersUseCase mGetUsersUseCase;

    @Inject
    UserViewModelMapper mUserMapper;

    @Inject
    public UsersPresenter() {
    }

    @Override
    public void attachView(UsersContract.View mvpView) {
        super.attachView(mvpView);
    }

    @Override
    public void detachView() {
        super.detachView();

        mGetUsersUseCase.unsubscribe();
    }

    @Override
    public void fetchUsers(boolean forceRefresh) {
        getMvpView().showProgress();

        mGetUsersUseCase.execute(forceRefresh, new DisposableObserver<List<User>>() {
            @Override
            public void onNext(List<User> users) {
                getMvpView().hideProgress();
                getMvpView().showUsers(mUsersMapper.mapUsersToViewModels(users));
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onError(Throwable e) {
                getMvpView().hideProgress();
                getMvpView().showErrorMessage(e.getMessage());
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

UseCaseObservableWithParameter

public abstract class UseCaseObservableWithParameter<REQUEST_DATA, RESPONSE_DATA, REPOSITORY> extends UseCase<Observable, REQUEST_DATA, RESPONSE_DATA, REPOSITORY> {

    public UseCaseObservableWithParameter(REPOSITORY repository, Scheduler threadScheduler, Scheduler postExecutionScheduler) {
        super(repository, threadScheduler, postExecutionScheduler);
    }

    protected abstract Observable<RESPONSE_DATA> buildObservable(REQUEST_DATA requestData);

    public void execute(REQUEST_DATA requestData, DisposableObserver<RESPONSE_DATA> useCaseSubscriber) {
        this.disposable.add(
                this.buildObservable(requestData)
                        .subscribeOn(threadScheduler)
                        .observeOn(postExecutionScheduler)
                        .subscribeWith(useCaseSubscriber)
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

用例

public abstract class UseCase<OBSERVABLE, REQUEST_DATA, RESPONSE_DATA, REPOSITORY> {

    protected final REPOSITORY repository;

    protected final Scheduler threadScheduler;

    protected final Scheduler postExecutionScheduler;

    protected CompositeDisposable disposable = new CompositeDisposable();

    public UseCase(REPOSITORY repository,
                   @Named("Thread") Scheduler threadScheduler,
                   @Named("PostExecution") Scheduler postExecutionScheduler) {
        Timber.d("UseCase CTOR");
        this.repository = repository;
        this.threadScheduler = threadScheduler;
        this.postExecutionScheduler = postExecutionScheduler;
    }

    protected abstract OBSERVABLE buildObservable(REQUEST_DATA requestData);

    public boolean isUnsubscribed() {
        return disposable.size() == 0;
    }

    public void unsubscribe() {
        if (!isUnsubscribed()) {
            disposable.clear();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

pla*_*ist 9

一个问题中有很多问题。让我尝试巩固我认为是您的关键问题的内容

  • 实体可以互相引用吗?答案将是:是的。同样,在Clean Architecture中,您可以创建一个域模型,其中实体相互连接

  • UseCase应该返回什么?答:用例定义了最适合用例的输入DTO(数据传输对象)和输出DTO。鲍伯叔叔在书中写道,不应将实体传递给用例或从用例中返回

  • 那么,主持人的角色是什么?答:理想情况下,演示者仅在转换数据。它将一层最方便的数据转换为另一层最方便的数据。

希望本指南能帮助您回答您的详细问题

您可以在我最近的帖子中找到更多详细信息和示例:https : //plainionist.github.io/Implementing-Clean-Architecture-UseCases/ 和 https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter /

  • 感谢你的回答。关于用例,几乎我发现的清洁架构实现的每个示例都返回实体,然后呈现器将实体映射到视图模型。这只是为了避免实体和世界其他地方之间出现另一个 dto 对象吗? (3认同)