为什么要先介绍MVC?
如果你要想更佳深刻的理解MVP,并在实际开发中灵活的应用,那么就要先了解它的低配版MVC,他俩只是一步之遥,先了解MVC再学习MVP,MVP的优势才能凸显出来,这样连贯性的学习才会加深对MVP的理解。
目录
MVP那些事儿(5)……中介者模式与MVP的关系【知识点】
MVP那些事儿(6)……MVC变身为MVP
MVP那些事儿(7)……Kotlin实现MVP【知识点】
MVP那些事儿(8)……当MVP遇到Lifecycle【知识点】
MVP那些事儿(9)……探究MVP的最佳实践
MVP那些事儿(10)……MVVM双向绑定
MVP那些事儿(11)……基于MVVM的Architecture Components
我们在上一章提到,MVP那些事儿(2) 用场景说话 对我们的案例进行MVC的设计,也就是第一步,如何实现一个MVC,在实现之前我们先搞清楚几点概念。
MVC的作用?它是设计模式吗?是框架吗?是架构吗?
MVC属于分层架构中的一员,无论是哪一个层从复用性和扩展性都是由当前业务所限制的,如果一开始就从复用和扩展上作为出发点来使用MVC的话,一定要有足够的开发经验(并不是不可以,分层架构也是可以有扩展性的,但要考虑主次),除非你非常明确未来软件的复杂度。举个例子,比如有一个算法,它处理一组数据所需的时间关系如下:10条,需要1秒,20条需要1.8秒,30条需要2.5秒,,,,100条需要6秒,这个时间复杂度可以看出什么,可以看出这个算法处理的数据量越大,它的处理所消耗的时间递增越缓慢,也就越是大数据量越能体现它的价值,再举一个实际中的例子,人均100的餐厅,两个人去也许每个人只能尝到2~3种菜样,但四个人去,每个人会尝到4~6菜样,20个人去,你也许可以把他们家的菜尝个遍,MVC也是如此,软件的复杂度越高,越能体现MVC的扩展价值,这就是我为什么要讲不要一上来就想从扩展性作为使用MVC的出发点,除非你是一个很有经验的人,能提前估计出项目的一个大致的复杂度。更稳妥的方式应该是在项目的推进中不断的优化自己的架构,很多萌新会进入到误区,一看MVP大家都在用,自己设计一个吧,到最后发现根本就你一个人在吃菜,更关键的是别人想和你吃也吃不了,不通用,根本无法体现它的扩展价值,负责人发现后第一反应是,你搞这么多接口抽象类干什么?就你一个人用,搞事情。
MVC不是简单的设计模式
一个只为了分层就可以被叫做设计模式的话,那么外观模式看来还不是最尴尬的模式,所以它不能简单的称呼为设计模式,因为它的范围和深度更加立体,称为架构更为妥当,当然也可以称之为复合设计模式,它里面包含了,策略,组合,观察者,等设计模式。那么,架构和框架的关系又是什么呢?
架构和框架说的不是一回事儿
MVC是一种架构思想,而基于这个思想的框架也叫MVC框架,在ios的开发中,系统为我们实现了公共的视图类UIView,和控制器类UIViewController,还有大名鼎鼎的Struts2框架,都是基于MVC架构设计出来的框架(基于MVVM架构的DataBinding,Architecture Components框架),框架是一个有边界的可扩展集,它在可扩展的同时也是有边界的,也就是说你只能在这个框架里玩耍,而无法超出这个框框。如果你只是在项目中使用了MVC的思想去做分层,那么这个项目或模块用的就是MVC架构设计,如果你用了Struts框架,那么这个项目使用了MVC框架。当你向别人介绍你的项目时,你可以这么说:我的项目使用的是MVVM架构,具体用到了Architecture Components作为框架来进行开发,千万不要说成:大家好,我来介绍一款基于MVVM框架设计的Architecture Components架构,这听的得多别扭啊。
一句话总结——架构是蓝图,而框架是实实在在的产品,MVP架构就一个,而基于MVP架构设计的框架可以是千千万万个。
以上就是对MVC的一个大致的介绍,学习MVC之前,要先会写,学会了写,再学习用,接下来我们先实现一个MVC框架。
实现MVC什么?MVC(框架)
在第一章的场景里我们挑一个需求,我们来实现一个向服务端请求数据,并显示在列表的需求。
在Android中我们实现上面的需求,通常情况是在界面的Create时,调用一个请求接口,通过异步的方式返回数据,待数据返回时更新UI,我们用一个Activity来实现:
public class TasksActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//...初始化
//请求列表数据
Data data = loadData();
}
void onDataCallBack (Data data) {
//更新列表
upDateList(data);
}
}
这是一段简单的示例代码,通常情况下我们用像loadData这样的方法去请求数据,并且绑定监听,监听数据的返回,当数据返回时,像onDataCallBack这样的回调方法会被触发,同时进行ui的更新。
同样的需求,用MVC写一遍
MVC分别为Model,View,Controller,顾名思义,首先我们需要创建这三个对象,第二步,我们把这三个对象组合起来,我们先一个一个的认识它们,逐个击破,首先从简单的来,Model,
什么是Model,它的职责
逻辑层(领域层)Domain Object,逻辑执行体,除此之外也可以兼顾JavaBean的职责,但Model层可不是简单的Value Object。而 JavaBean也就是Value Object,只是用来封装数据,不具备Model层的职责,当然也可以把JavaBean当作Model类的一个内部类来使用,当然也可以单独一个类,通常我们也会这么作,方便复用。
萌新⚠️
1、JavaBean不是Model,但Model也可以包含JavaBean的职责,但不是必须的。
2、Model是用来处理数据的,如获取数据(本地或者服务端),数据处理,如CURD。
按照上面的定义我们来写一个Model
/**我是一个Model**/
public class TasksRepository {
//从服务器请求获取数据
void getTasks() {}
//从内存缓存获取数据
Data getTaskCache() {}
//从磁盘缓存获取数据
Data getTaskDiskCache(){}
//保存一条数据
boolean saveTask(Task task) {}
//对数据进行排序
Data orderData(Data data, int orderType){}
}
首先我们创建一个Model类名为TasksRepository,首先不要关心里面的Data和Task对象,它们只是普通的Bean对象,TasksRepository里面包含了五个方法,按照之前的定义,它是有获取数据的职责的,所以这其中三个方法都是和获取数据相关,比如getTasks,你可以调用网络组件进行数据的获取,它还可以对数据进行CURD的操作,比如savaTask的方法用来保存一条数据,还可以对数据进行业务处理,比如用orderData方法对数据进行重新的排序。
一句话总结,Model负责获取数据,操作数据,和对数据进行业务处理。
什么是View,它的职责
接下来我们讲一讲View,View就是我们的视图层。
1、它的主要职责为呈现Model的数据、主动询问状态或被动的监听
2、通知控制器Controller去处理一些事情
3、接收Controller,编辑自己与Model无关的状态
按照View的职责我们来设计一下这个类
/**我是一个View**/
public class TaskView {
//当列表初始化后,告诉控制器该加载数据了
void viewCreate() {
controller.loadNomData();
}
//更新列表
void upDateList() {
//主动请求Model获取数据
Data data = tasksRepository.getTaskCache();
//更新ui
list.update(data);
}
void beginLoadData(){
list.showHead();
}
}
在TaskView中也就是我们的视图对象,它包含了俩个对象,分别为controller和taskeRepository,controller就是控制器对象,我们下一个讲它,taskeRepository就是我们的模型,上面提到过它,先不要关心这两个对象是怎么初始化来的,要关心的是View视图对象是包含了C和M的,按照职责它必须这么做,首先像viewCreate这类方法一般是在界面初始化时调用的(在Android 中可能是Activity或者Fragment初始化时调用的,也可能是某一个执行动作),让controller去执行loadNomData的方法请求数据,这里唯一需要注意的是,并没有明确的告诉controller请求的数据是从什么地方来的,也许是缓存,也许是网络请求,view是无需关心这一点的。同时还要注意的是,在讲解Model的时候已经明确了Model是有获取数据的职责,但是在上面的示例中,为什么是controller去负责获取数据,而不是用Model?也就是我们的tasksRepository对象?请大家记住这个疑问,会在讲解Controller时回答。
第二个方法upDataList方法,
//更新列表
void upDateList() {
//主动请求Model获取数据
Data data = tasksRepository.getTaskCache();
//更新ui
list.update(data);
}
它的内部是通过tasksRepository对象的getTaskCache()方法获取数据,这个方法在Model的定义里面,大家还有印象吧。讲到这里,大家可能发现有一个严重的漏洞,view在执行tasksRepository.getTaskCache()时,是怎么知道这个数据已经准备好了呢?看view的职责1:
主动的问询或者监听Model
我们把调用tasksRepository.getTaskCache()看作为主动的问询,而在主动问询前,View应该得到一个有效的通知,这个通知应该由Model发起,当监听到Model:我的数据准备好了,你来拿吧时,View会去主动的向Model获取数据。举个现实中的例子,你在网上买东西,现在一般都往快递柜里投放,等到短信通知你快递到了时,你才会去快递柜里拿商品,但也许你实在等不及了,也可以天天打电话,我们就不详细讨论了,View和Model但关系也就是观察者与被观察者的关系。可是,在上面的示例中我们没有写出监听Model的代码,请大家记住这个疑问,我会在后面讲解三者的依赖绑定时会揭晓答案。
最后一个方法,beginLoadData(),
void beginLoadData(){
list.showHead();
}
当开始请求数据时,list会显示自己的头布局,像beginLoadData这样编辑view本身的方法在实际开发过程中还有很多,它们关注的是控件本身的状态,最重要的,这样的方法可能会被Controller随时的调用,我们在讲解Controller时进行讲解。
什么是Controller,它的职责
Controller,也就是我们的控制器,它把视图的操作发送到模型。
接收View的操作,并转调给Model
改变View的状态
按照上面的职责,我们尝试的设计一下这个类:
/**我是一个Contorller**/
public class TasksController {
void loadNomData() {
if(tasksRepository.getTaskCache() == null){
//执行Modle
tasksRepository.getTasks();
//执行View
view.beginLoadData();
}
}
}
在对Controller讲解之前,我们先停顿一下,在之前的介绍中我们好像遗留了几个问题,在介绍View时,我用加租文字的方式标示了三个问题,在介绍Controller时我们将要解决掉问题1,和问题3。我们一起回顾一下这2个问题:
疑问1,在讲解Model的时候已经明确了Model是有获取数据的职责,但是在上面的示例中,为什么是controller.loadNomData()去负责获取数据,而不是用Model?也就是我们的tasksRepository对象?
这个问题要从Contrller的职责说起:接收view的操作,并转调给Modle。什么是view的操作,比如list的下拉刷新操作,转调给Model,也就是说其实Controller并没有实际的实现加载数据的代码,而是让Model去执行,解释完以后,看一下TasksController里的第一个方法loadNomData(),
void loadNomData() {
if(tasksRepository.getTaskCache() == null){
//执行Modle
tasksRepository.getTasks();
//执行View
view.beginLoadData();
}
}
它内部第一条语句:
tasksRepository.getTasks();
而tasksRepositor对象就是我们的Model,它执行了getTasks()的操作,其实controller的loadNomData()方法只是对这个过程进行了一个封装。听完上面的解释,我们知道Controller是这么设计的,可是又一个问题出现了,虽然我们知道了Controller转调了Model的方法,可是我为什么要这么做?我直接在View里调用tasksRepository.getTasks();不可以吗? 清大家记住这个问题,我下一章为大家解答,我们先学会写,再去研究怎么用。
疑问2,像beginLoadData这样编辑View本身状态的方法在实际开发过程中还有很多,它们纯粹关注的是控件本身的状态,最重要的,这样的方法可能会被Controller随时的调用。
我们先回顾一下这个方法beginLoadData()
//controller会随时调用
void beginLoadData(){
//显示列表的头部,改变了列表的属性
list.showHead();
}
我们还是从Controller的职责来说,Controller有权利对View的状态进行改变,不管是通知的形式,还是直接的访问。而且View的职责里也说明了,它是可以接收Controller更改自己的状态,而更关键的一点这个状态的改变是无需依赖Model的,更加的纯粹。
有了上面问题的承上启下,我们再回头看一下TasksController里的loadNomData()方法,它首先判断了是否可以从tasksRepository里拿到缓存,如果没有就执行getTasks(),同时通知view我要开始加载数据了,view会在beginLoadData()方法中对自己的列表控件进行一个显示头部的处理,相当规范的一个封装。
总结:到此,我们已经学习完了MVC这三个层的定义和职责。在文章的开头我有讲过,要想用MVP,就要先用MVC,要想用MVC就要先会"写"MVC,写才是第一步,我们首先把MVC这三个层,拆分成三个片段,每一个片段都规范好它们的职责,然后我们再想办法把它在组装在一起,在下一章我们要解决以下问题:
1、如何把这三个片段组装起来?在Android里怎么写?
2、view在执行tasksRepository.getTaskCache()时,是怎么知道tasksRepository数据已经准备好了呢?怎么通知view的?
谢谢大家关注和评论,更新的有些慢,不如关注我一下,随时知道更新进度。