C.L.Wang
任职于春雨医生
2016.10.31
By C.L. Wang
MVC - Model-View-Controller
MVP - Model-View-Presenter
MVVM - Model-View-ViewModel
未使用架构的项目
1. 未分离界面(UI)逻辑与业务(Business)逻辑.
2. 版本迭代导致所有相关类, 不断需要修改.
3. 模块高耦合, 存在隐藏Bug, 无法编码测试.
架构目标就是为了解决这些问题!
在被动模式(即Controller是唯一操作Model的类)中:
基于用户的响应事件, Controller通知Model更新数据.
在Model更新后, Controller通知View更新UI, View从Model中获取数据.
在主动模式(即Controller不是唯一操作Model的类)中:
在更新时, Model层使用观察者(Observer)模式通知View和其他类.
View实现观察者的接口, 在Model中, 注册成为观察者, 接收通知.
当Model层发生数据更新时, 告知全部观察者, View从Model中更新数据.
在早期开发中, Activity(或Fragment)既是View又是Controller, 并未进行分离. 有些剥离出Model, 独立于平台进行测试.
在使用MVC框架时, Activity(或Fragment)代表View, 并从中剥离出不含任何的Android类(如Context等)的Controller.
对比于原始项目, MVC架构具有:
修改UI逻辑时, 较少修改Model; 修改业务逻辑时, 较少修改View.
MVC模式, 分离类的UI与业务职责, 增加可测试性与可扩展性.
// View处理UI逻辑
String docName = userModel.getName();
String docClinic = userModel.getClinic();
nameTextView.setText(docName + ", " + docClinic)
// Model处理UI逻辑
String nameAndClinic = userModel.getNameAndClinic();
nameTextView.setText(nameAndClinic);
在被动模式中, Controller通知Model更新数据, 并通知View显示.
对于UI逻辑, 如果View处理, 单元测试会遗漏逻辑; 如果Model处理, 则隐式地依赖于View, 导致模块增加耦合.
在主动模式中, 每个UI逻辑都需要增加观察者, 保证正确更新.
View既依赖于Controller又依赖于Model. 在修改UI逻辑时, 也需要修改Model, 降低架构的灵活性.
Model层负责获取或存储在远程或本地的数据.
例如, 在处理数据时, Model先检索本地数据, 为空则请求网络数据, 并同步本地, 再显示.
在构造器中, 添加本地与远程数据源的接口类, 源内分离逻辑与实现.
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}
与Presenter配合使用, 负责展示数据, 通知Presenter响应用户事件.
Activity(或Fragment)是View层. View与Presenter相互对应. View继承自含有设置Presenter的接口.
public interface BaseView<T> {
void setPresenter(T presenter);
}
public interface TasksContract {
interface View extends BaseView<Presenter> {
// ...
}
// ...
}
View截获事件, 通过Presenter接口传递事件给Presenter处理.
完成后, Presenter通过View接口传递数据给View显示或反馈.
Presenter与View一起创建, 并绑定View与Model的引用.
通过View接口, 在构造器中, 创建View与Presenter的相互引用.
public TasksPresenter(@NonNull TasksRepository tasksRepository,
@NonNull TasksContract.View tasksView) {
mTasksRepository = tasksRepository;
mTasksView = tasksView; // 引用View
mSubscriptions = new CompositeSubscription();
mTasksView.setPresenter(this); // 在View中设置Presenter的引用
}
Presenter请求Model, 获取数据, 根据UI逻辑, 在View中显示.
Presenter方法覆盖全部事件处理逻辑, 与View事件相互对应.
MVP架构更好地分离UI与业务职责, 解除逻辑之间的耦合.
对于小型项目而言, 与设计模式类似, 会导致过度设计, 增加代码量.
当处理复杂页面时, Presenter层会包含大量UI逻辑与业务逻辑, 非常冗余, 并违反单一职责原理.
MVP架构的核心在于Presenter层. Presenter打破Model与View之间的耦合, 创建展示数据的通道, 隔离业务逻辑, 支持单元测试.
MVVM与MVP相似, 目标都是分离UI与业务逻辑.
Model, 即DataModel:
通过事件流提供多种数据源, 如网络数据, 数据库, 首选项(Shared Preferences)等, 负责全部的业务逻辑.
Model提供泛化数据的接口, 确保业务逻辑独立完整, 被不同页面共享与使用, 为ViewModel提供数据.
ViewModel提供数据更倾向于View, 从Model中获取必要的数据, 在封装UI逻辑后, 供给不同View展示.
ViewModel有两点需要注意:
View负责展示数据, 如Activity或Fragment.
在onResume绑定ViewModel, 在onPause解绑.
更新逻辑, 在ViewModel中处理, View仅负责展示数据.
private final Subscription mSubscription = new Subscription();
@Override
public void onResume() {
super.onResume();
mSubscription.add(mViewModel.getData()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::updateView,
this::handleError));
}
@Override
public void onPause() {
mSubscription.clear();
super.onPause();
}
MVVM重要特性就是解耦.
Model不含Android类, 只含业务逻辑, 允许单元测试.
ViewModel在Model上封装UI逻辑, 不含Android类, 允许单元测试.
ViewModel需绑定Model, 允许自由替换Model数据源.
public class ViewModel {
private final IDataModel mDataModel;
public ViewModel(IDataModel dataModel) {
mDataModel = dataModel;
}
public Observable<Data> getData() {
return mDataModel.getData();
}
}
MVVM对比于MVP, 优势是进一步解耦UI逻辑与业务逻辑.
MVC: http://www.jianshu.com/p/1912473dff9a
MVP: http://www.jianshu.com/p/c1c546724e4d
MVVM: http://www.jianshu.com/p/6c026c5b4cfd
GitHub: https://github.com/SpikeKing
1.1K Followers; China Java No. 24
By C.L.Wang
讲述在项目中MVC/MVP/MVVM三种架构模式的基本原理与使用方法.