好的,所以我刚开始一个新的Android项目,并想尝试实现Uncle Bob的Clean Architecture。我已经使用RxJava、GitHub示例和样板以及Fernando Cerjas的博客(比如这篇文章)有了良好的开端,但仍然有一些关于如何实现某些UseCases的问题。
简短概述
一个实体是否应该具有另一个实体的字段(例如,在我的例子中,User
具有一个List<Messages>
字段)?
或者,Presenter是否应该组合UseCases以构建映射到多个实体的ViewModel(那么,你如何编写映射器代码)?
还是Presenter应该将每个UseCase/Entity与一个ViewModel相关联,并创建某种"等待所有数据onNext"来为每个ViewModel调用view.show()?
基本上,UseCases只应该返回Entities吗?一个实体可以由其他实体组成(如类的字段)吗?Entities只是无脑的数据模型POJO吗?如何表示“join SQL”查询?
以一个简单的用户/消息应用为例。
UserList
显示一个Users
列表。UserDetails
显示用户的信息以及其最新的消息。
UserList
非常简单,我可以看到如何编写相关的UseCase和层(下面的代码)。
我的问题在于UserDetails
屏幕上。
如果我希望所有数据同时传递给视图(例如构建由User类组成的ViewModel,带有一个List字段),应该如何编写GetUserInfoUseCase
?GetUserInfoUseCase
的返回值应该是什么?应该编写Observable<User> GetUserInfoUseCase
和 Observable<List<Message>> GetUserLatestMessages
然后在Presenter中合并它们吗?如果是,我该如何管理这个,因为我的Presenter中没有Observables(我只传递Observer作为我的UseCases参数)?
User Entity
public abstract class User {
public abstract long id();
public abstract String name();
...
}
消息实体
public abstract class Message {
public abstract long id();
public abstract long senderId();
public abstract String text();
public abstract long timstamp();
...
}
获取用户用例
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();
}
}
用户展示器
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());
}
});
}
}
使用带参数的UseCaseObservable
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)
);
}
}
使用案例
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();
}
}
}