MVC、MVP、MVVM设计架构
架构的目的
程序模块化,做到模块内部的高举和和模块之间的低耦合。 提高开发效率,并更容易进行后续的测试以及定位问题。考虑实际情况去进行架构的选择。
MVC设计架构
模型-视图-控制器,业务逻辑、数据、界面显示分离的方法组织代码,改进和个性化定制界面与用户交互的同时,不需要重新编写业务逻辑。
View–最上面的一层,处理界面的显示结果
Model–最底下的一层,处理业务逻辑,数据等
Controller–中间的一层,从View输入指令,选取Model中的数据,然后进行操作,产生最终结果。处理界面控制点击事件的操作
这三层紧密联系,有相互独立,每一层内部变化不影响其它层。修改内部的数据也不会影响到其它层,方便了维护和升级。替考扩展性和维护性,可以设计接口,重写方法的方式。
在生活中的理解
例如一家商店,商店内的仓库就是Model层,商店外部的铺面就是View层,而仓库与铺面的换货上架之类的操作就是Controller层。
Android中的MVC
- 视图层(View):就是Layout,一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。
- 控制层(Controller):就是监听器操作。Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
- 模型层(Model):放实体类的地方。对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。与View无关。
优点
- 分层结构清晰,有利于代码的管理和维护
- 降低层与层之间的依赖,方便数据修改,降低耦合度。一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可
- 提高扩展性和复用性,像多个视图能够共享一个模型。将数据和业务规则从表示层分开,就可以最大化从用代码。
缺点
- 增加了系统结构和实现的复杂性,降低系统的性能。
- 视图与控制器间的过于紧密的连接,影响他们的独立重用
- 视图对模型数据的低效率访问,操作需要通过中间层完成
- 目前,一般高级的界面工具或构造器不支持MVC架构。
- 花费时间去思考如何运用MVC,MVC并不适合小型甚至中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
MVP设计架构
View——处理用户时间和视图部分
Model——访问数据
Presenter——连接View和Model的桥梁(或适配)
关键是,高层接口不知道底层接口的细节,或者更准确地说,高层接口不能,不应该,并且必须不了解底层接口的细节,是(面向)抽象的,并且是细节隐藏的。
优点
- 可测试(TDD)
- 可维护(代码复用)
- 容易Review
- 信息隐蔽
缺点
- 冗余的,尤其是小型App开发
- (有可能)额外的学习曲线
- 开始编写代码之前需要时间成本(设计架构是所有项目开发所必需的)
例子
为每一个View定义接口
public interface TopView {
void initViews();
void openDatePickerDialog();
void startListActivity();
}
重写TopView类
public class TopActivity extends Activity implements TopView {
// here we use ButterKnife to inject views
/**Calendar Title */
@Bind(R.id.calendar_title)
TextView mCalendarTitle;
private TopPresenter mTopPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top);
ButterKnife.bind(this);
// Save TopPresenter instance in a meber variable field
mTopPresenter = new TopPresenter();
mTopPresenter.onCreate(this);
}
/*
* Overrides method from the {<a href="http://www.jobbole.com/members/57845349">@link</a> TopView} interfaces
*/
@Override
public void initViews() {
// Actionbar settins
// set event listeners
}
@Override
public void openDatePickerDialog() {
DatePickerFragment.newInstance().show(getSupportFragmentManager(),
DatePickerFragment.TAG);
// do not write logic here... all logic must be passed to the Presenter
mTopPresenter.updateCalendarDate();
}
@Override
public void startListActivity() {
startActivity(new Intent(this, ListActivity.class));
}
}
这是Presenter类,最重要的一点是Presenter仅仅是连接View与Model的适配桥梁。
比如,TopUseCase#saveCalendarDate()是对TopPresenter细节隐藏的,同样对TopView也是如此。你不需要关心数据结构,也不需要关心业务逻辑是如何工作的。因此你可以对TopUseCase执行单元测试,因为业务逻辑与视图层是分离的。
public class TopPresenter {
@Nullable
private TopView mView;
private TopUseCase mUseCase;
public TopPresenter() {
mUseCase = new TopUseCase();
}
public void onCreate(@NonNull TopView topView) {
mView = topView;
// here you call View's implemented methods
mView.initViews();
}
public void updateCalendarDate() {
// do not forget to return if view instances is null
if (mView == null) {
return;
}
// here logic comes
String dateToDisplay = mUseCase.getDateToDisplay(mContext.getResources());
mView.updateCalendarDate(dateToDisplay);
// here you save date, and this logic is hidden in UseCase class
mUseCase.saveCalendarDate();
}
}
两种模式的主要区别:
- (最主要区别)View与Model并不直接交互,而是通过与Presenter交互来与Model间接交互。而在MVC中View可以与Model直接交互
- 通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。而Controller是基于行为的,并且可以被多个View共享,Controller可以负责决定显示哪个View
- Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
MVVM
MVVM是MVP的升级版,Model–View–ViewModel
ViewModel可以理解成是View的数据模型和Presenter的合体,ViewModel和View之间的交互通过Data Binding完成,而Data Binding可以实现双向的交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。
MVVM 是从 MVP 的进一步发展与规范,MVP 隔离了MVC中的 M 与 V 的直接联系后,靠 Presenter 来中转,所以使用 MVP 时 P 是直接调用 View 的接口来实现对视图的操作的,这个 View 接口的东西一般来说是 showData、showLoading等等。M 与 V已经隔离了,方便测试了,但代码还不够优雅简洁,所以 MVVM 就弥补了这些缺陷。在 MVVM 中就出现的 Data Binding 这个概念,意思就是 View 接口的 showData 这些实现方法可以不写了,通过 Binding 来实现。
三者的差异
Controller
Controller接收View的操作事件,根据事件不同,或者调用Model的接口进行数据操作,或者进行View的跳转,从而也意味着一个Controller可以对应多个View。Controller对View的实现不太关心,只会被动地接收,Model的数据变更不通过Controller直接通知View,通常View采用**观察者模式监听**Model的变化。Presenter
Presenter与Controller一样,接收View的命令,对Model进行操作;与Controller不同的是Presenter会反作用于View,Model的变更通知首先被Presenter获得,然后Presenter再去更新View。一个Presenter只对应于一个View。根据Presenter和View对逻辑代码分担的程度不同,这种模式又有两种情况:Passive View和Supervisor Controller。ViewModel
注意这里的“Model”指的是View的Model,跟MVVM中的一个Model不是一回事。所谓View的Model就是包含View的一些数据属性和操作的这么一个东东,这种模式的关键技术就是数据绑定(data binding),View的变化会直接影响ViewModel,ViewModel的变化或者内容也会直接体现在View上。这种模式实际上是框架替应用开发者做了一些工作,开发者只需要较少的代码就能实现比较复杂的交互。
整体架构
代码和文档规范,根据需求进行模块划分,确定交互方式,形成接口文档,这些较为通用的内容不再细说。做Android App时,一般将App进行纵向和横向的划分。纵向的App由UI层,逻辑层和模型层构成,整体结构基于MVP思想。
UI层内部多用模板方法,以Activity为例一般有BaseActivity,提供包括一些基础样式,Dialog,ActionBar在内的内容,展现的Activity都会继承BaseActivity并实现预留的接口,Activity之间的继承不超过3次;为避免Activity内代码过多,将App的整体控制权后移,也借鉴了IOC做法,大量的逻辑操作放在逻辑层中,逻辑层和UI层通过接口或者Broadcast等实现通信,只传递结果。一般Activity里的代码量都很大,通过这两种方式一般我写的单个Activity内代码量不超过400行。
逻辑层实现的是绝大部分的逻辑操作,由UI层启动,在内部通过接口调用模型层的方法,在逻辑层内大量使用了代理。打个比方,UI层告诉逻辑层我需要做的事,逻辑层去找相应的人(模型层)去做,最后只告诉UI这件事做的结果。
模型层没什么好说的,这部分一般由大量的Package组成,代码量是三层中最大的,需要在内部进行分层。
横向的分割依据AOP面向切面的思想,主要是提取出共用方法作为一个单独的Util,这些Util会在App整体中穿插使用。很多人的App都会引入自己封装的Jar包,封装了包括文件、JSON、SharedPreference等在内的常用操作,自己写的用起来顺手,也大幅度降低了重复作业。
这样纵,横两次对于App代码的分割已经能使得程序不会过多堆积在一个Java文件里,但靠一次开发过程就写出高质量的代码是很困难的,趁着项目的间歇期,对代码进行重构很有必要。