MVP那些事儿(3)……在Android中使用MVC(上)服务器
MVP那些事儿(4)……在Android中使用MVC(下)架构
若是你要想更佳深入的理解MVP,并在实际开发中灵活的应用,那么就要先了解它的低配版MVC,他俩只是一步之遥,先了解MVC再学习MVP,MVP的优点才能凸显出来,这样连贯性的学习才会加深对MVP的理解。布局
回顾MVP那些事儿(2) 初探MVC架构的内容:post
MVC的做用?它是设计模式吗?是框架吗?是架构吗? MVC各个层的定义和职责学习
经过上一篇的内容,你们也对MVC已经有了一个大体的了解,在开启这一章内容前,但愿你们能先阅读上一篇的内容,不然可能会“断片”。 在上一篇中,咱们详细的介绍了MVC三个层的基本职责,咱们再回顾一下这三个“片断”:
一、JavaBean不是Model,但Model也能够包含JavaBean的职责,但不是必须的。 二、Model是用来处理数据的,如获取数据(本地或者服务端),数据处理,如CURD。
一、它的主要职责为呈现Model的数据、主动询问状态或被动的监听 二、通知控制器Controller去处理一些事情 三、接收Controller,编辑本身与Model无关的状态
一、接收View的操做,并转调给Model 二、改变View的状态
同时还遗留了两个问题:
一、如何把这三个片断组装起来?在Android里怎么写? 二、view在执行tasksRepository.getTaskCache()时,是怎么知道tasksRepository数据已经准备好了呢?怎么通知view的?
在模式设计时,咱们会经过关系图来反映对象间的关系,以下图:
这是一张很是经典的MVC架构图(手残,有点丑……),相信你们对这张图已经不陌生了,OK,这张图我先放下不表,由于考虑到咱们介绍的是架构,自己是抽象的,加上如此抽象的图,怕是不能好好的切入,因此我想尽量的巨像化这个过程,当你们有一个清晰的理解后,再回过头来从新审视它。我原本是想放在文章的后半段祭出这张图的,但想了想有点不妥,缘由是咱们上期已经经过一整篇用来初探MVC,也该把MVC的架构图贴出来应一应景了……
咱们依旧经过现实中的场景来描述一下MVC三者的关系,让你们对这三者的关系有一个初期的直观的理解,起到一个抛砖引玉的效果,废话很少,场景开始。
想必你们都租过房子,有当租客的体验,也有一部分人可能还有当过过房东经验,在当下的社会中,房屋中介是咱们租房找房必不可少的环节,我相信大部分人都和中介打过交道,咳咳,那咱们就还原一下租房的过程,
在这个场景里包含了三个角色,租客、中介、和房东,
咱们从租户的角度描述一下租房的场景,只考虑正向流程。
租客要先找到中介并描述本身须要的房子,中介接到需求后,经过筛选发现有一个房东很适合,因而中介开始与这个房东接触,询问一些和房子租金相关的事宜,若是中介觉的合适,他会把反馈告知客户,若是客户也觉的合适,中介就会把租客和房东约在店里进行更进一步的协商,若是能促成合同,那么到此为止中介完成了现阶段的工做,剩下的事情,好比租客向房东每一个季度缴纳房租,或者询问房东家里电器的使用方式,亦或者房东询问房客租约到期是否续租等等,这些沟通均可以抛开中介去完成,也就是说,不必定须要中介做为他们之间沟通的桥梁,但也并不意味着中介在之后的场景中是不可用的,好比,在租房过程当中有一些事情租客和房东有分歧,也能够经过中介来进行协调和沟通。
还有一种中介的形式是,租客和房东是见不到彼此的,全程由中介负责,这种房叫托管房,租客不知道房东是谁,房东也不知道租客是谁,合同都和中介签,咱们如今不讨论这种状况。
这张图的目的一来是复述一下上面的场景描述,二来是让你们直观的看到它们三者交互的一个关系。
经过上诉场景的描述,和前一篇帖子中介绍的MVC各个对象的职责,想必他们到底谁是谁一目了然,咱们试着关联一下这几个对象
首先看中介,他负责了执行租客的招租要求,并告知房东这些要求,也就是说他做为中介向房东转告租客的需求,那中介就是咱们的Controller。
再次看租客,他是需求的发出者,他比其余几个对象更积极的提出需求,就比如View,老是再向Controller提出需求,也会更主动的向Model询问状态,就像租客同样,遇到问题,要么找中介,要么去询问房东,因此,在这里把租客看做是View更加的贴切。
最后只剩下房东这个角色了,最后的Model就分配给房东吧。
一、View To Controller 求租房
上面这五张图,是把MVC三个对象间的关系,以及案例中的业务流程作了一个“融合”,是一个抽象到具象的“融合”,架构图中这些带箭头的线,是用来解释对象间关系的,无非就是那些谁依赖谁的解释,我同时又在线上加入了一些事件,是但愿能把上面具象的问题引到抽象的架构中来,在实际中,这样的融合是不符合常理的,好比你没法用单个业务场景去定义对象间的依赖关系,好比View To Controller,难道只有求租房这个场景才能够这么用吗?显然不是的,包括各个角色中括号后面的称呼。因此,为了解决通用性的问题,人们把这样的具象问题抽象成了一种依赖关系。
架构是蓝图,抽象的存在,而框架是具象的,它是为了解决具体某一类问题而设计的。
在某些场合里,须要咱们去介绍咱们的软件架构,这就须要咱们具有描述架构的能力,我觉的能够先描述一下具象的问题,再描述向上抽离的步骤,最后陈述抽象的产物,也就是咱们的架构,是一个不错的思路。
经过上面大篇幅的抛砖引玉,相信你们对MVC的架构有了一个初步的认识,那么接下来咱们开始真正的使用,还记得上一篇咱们使用了一个需求:
需求:列表加载数据而且展现
咱们依旧使用这个需求,由于前面定义好了三个层的类,接下来要作的是按照MVC的架构把他们拼装起来:
开始加入到实际开发阶段,咱们先建立一个Activity
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
复制代码
紧接着,咱们把上一篇定义好的三个MVC对象也加入进来
Model :TasksRepository
/**我是一个Model**/
public class TasksRepository {
//从服务器请求获取数据
void getTasks() {}
//从内存缓存获取数据
Data getTaskCache() {}
//从磁盘缓存获取数据
Data getTaskDiskCache(){}
//保存一条数据
boolean saveTask(Task task) {}
//对数据进行排序
Data orderData(Data data, int orderType){}
}
复制代码
View :TasksView
/**我是一个View**/
public class TaskView {
//当列表初始化后,告诉控制器该加载数据了
void viewCreate() {}
//更新列表
void upDateList() {}
//just for ui
void beginLoadData(){}
}
复制代码
Controller :TasksController
/**我是一个Contorller**/
public class TasksController {
void loadNomData() {}
}
复制代码
如今咱们有了Activity,TasksRepository、TasksView、TasksController
有了这些对象,咱们就能够进行组合了,咱们从什么地方下手呢?也许咱们能够从MVC的架构图中获得些启示,虽然架构图很简单,可是有箭头,有方向,在UML中,这些线和箭头即是记录着对象间的关系:
public class MainActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化MVC
TasksController controller = new TasksController();
TasksView view = new TasksView();
TasksRepository model = new TasksRepository();
//依赖关系
controller.setView(view);
view.setController(controller);
view.setModel(model);
model.setController(controller);
model.setView(view)
//执行业务
controller.loadNomData();
}
}
复制代码
我认可,我又开始抛砖引玉了,从这种写法来看,Activity到是像MVC的容器,它包含了MVC这三个对象,这么写有问题吗?在继续以前,引用一个概念,Is a…… and Has a
翻译过来,就是,我是什么,和我有什么
Is a 我是什么?
//我是什么动物?我是一只猫
class Animal implments Cat {
//喵喵叫
void miaomiao();
}
复制代码
Has a 我有什么?
//我有什么动物?我有一只猫,一只狗,一只豹子
class Animal{
public Animal(Cat cat) {
//来只猫
this.cat = cat;
//来个豹子
this.baozi = new Baozi();
}
//来只狗
void setDog(Dog dog) {
this.dag = dog;
}
}
复制代码
相信你们看出了其中的差异,回过头咱们看一下MainActivity,它是什么,它又有什么,首先它是一个Activity,其次,它有TasksRepository、TasksView、TasksController这三个对象,重点在Has a上,像这样的依赖耦合度是很是高的,基本没有扩展的余地,为了下降耦合,可不可让MainActivity少几个Has a?固然能够。
//我是什么动物?我是一只猫
class Animal implments Cat {
//喵喵叫
void miaomiao();
public Animal() {
//来个豹子
this.baozi = new Baozi();
}
//来只狗
void setDog(Dog dog) {
this.dag = dog;
}
}
复制代码
因此咱们让MainActivity变一下身,从MVC这三个对象TasksRepository、TasksView、TasksControlle中选一个变,这样强耦合的个数从3个变成了2个,那么选谁好呢? 目前主流的是选择View,和选择Controller,你们都是从业务的角度出发去选择,首先选择View,由于Activity里包含了布局对象,因此由于“离得近”让Activity变成View,选择Controller的是由于Activity自己的生命周期能够很好的用来控制业务流程,这样对Controller来讲更加有利,说实话,我并无深挖其中哪个更好,由于我觉的这两种方案都有他们的侧重,仍是要从实际的业务角度去考虑问题,那咱们突发奇想让Activity既是View,又是Controller呢?这不就解决变成谁的问题了吗?我的觉的仍是不要的好,你总不能介绍本身的时候说:你们好,我是猫,也是狗,我是男的也是女的,我相信到时候场面必定很混乱,咱们仍是保持类的职责单一性吧。但没准那天变成Model玩一下也能够的吧,因此说,目前我先从变成View来开始,后续的章节咱们会讲解变成Controller该怎么办。
更新UML
public class MainActivity extends AppCompatActivity implments TasksView{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//略……
}
}
复制代码
肯定了Activity,咱们才开始了第一步,接下来咱们就要看一下Controller的初始化,在此之间须要引用一个概念,依赖注入。
什么是依赖注入?首先解释一下什么是依赖,看下面的代码:
class Animal{
public Animal(Cat cat) {
//来只猫
this.cat = cat;
//来个豹子
this.baozi = new Baozi();
}
//来只狗
void setDog(Dog dog) {
this.dag = dog;
}
//猫猫叫一下
void miaomiao() {
cat.miaomiao();
}
//豹子叫一下
void aoao() {
baozi.called();
}
}
复制代码
aoao这个方法内部,调用了baozi这个对象叫的方法called(),也就是说,首先Animal这个对象本身是不会叫的,他须要依赖于Baozi对象,Animal依赖Baozi去叫,这个依赖就是Baozi,咱们看到Baozi对象的初始化,是在Animal构造器中new出来的。再看一下miaomiao方法内部,依旧是依赖于cat的miaomiao方法去叫,而这个cat对象是从构造函数传进来的,baozi和cat一样是Animal的依赖,但得到的途径不一样,cat的得到途径就是注入,而它又是依赖,因此咱们称之为依赖注入,依赖注入的形式除了经过构造器参数外,还能够经过普通方法传入,如上面的setDog方法,咱们注入了一只Dog。
说到依赖注入的好处,不得不提Java的面向对象的特性中的多态,举个例子,上面的代码中的Cat能够是一个接口,咱们在使用Animal类前,能够肯定一下Cat的实现类,根据业务需求,接收到的多是公猫,也多是母猫,多是美短,也多是英短(猫的品种),若是咱们把Cat的依赖放在Animal中去初始化,那么根据业务的变化,可能须要频繁的更改Animal类。第二点好处,当Cat的构造器发生变化,Animal也须要更改Cat,若是Cat须要另外的依赖,好比猫须要猫粮它才能活下去,那么Animal也要间接的依赖猫粮,对象间的耦合性就暴露了出来,因此从外部传入的依赖,Animal无需关心依赖的依赖。
看来依赖注入是很是好的设计手段,那么回到上面的问题,Contorller的初始化,咱们知道Controller是依赖于View的,因此咱们须要把View注入到Controller中去。
public class MainActivity extends AppCompatActivity implments TasksView{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Controller,this就是View,经过构造器注入
TasksController controller = new TasksController(tasksView:this);
}
}
复制代码
经过依赖注入,咱们把Controller和View的命运绑定在了一块儿。 说完了View -> Controller,咱们还要解决Controller -> View,其实咱们能够发现,Controller就是包含在View中的,由于Activity在上面已经变身成了View,而Controller也在Activity中经过new的方式进行了初始化,也就瓜熟蒂落的成了View的依赖了,只不过它不是经过注入的方式进入到View(MainActivity)中的,那么有什么办法能够作到这一点呢?固然有办法了,介绍一下依赖注入的第三种方式:经过构建一个Controller的工厂来负责生产Controller的实例,具体就不在这里细说了,之后讲到Dagger时能够和你们详细讨论。
那么Model(TasksRepository)的初始化放在哪里去作呢?以及它与Controller和View的关系又是怎样的呢?从MVC的架构图和框架的UML图里能够获得答案,Controller和Model之间是一个单向的依赖,也就是Controller -> Model,同时Model与View是一个双向的依赖,
一、Controller -> Model 二、Model -> View 三、View -> Model
咱们只需知足这三个条件,咱们直接这样,看代码:
public class MainActivity extends AppCompatActivity implments TasksView{
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Controller,this就是View,经过构造器注入
TasksController controller = new TasksController(tasksView:this);
//初始化Model,Model -> View and View -> Model
TasksRepository model = new TasksRepository(tasksView:this);
//Controller -> Model
model.setController(controller);
}
}
复制代码
首先在Activty中,初始化Model,并将View注入到Model里,这样,同时知足了Model -> View 和 View -> Model,其次,经过model的setController方法将上面建立好的Controller注入到Model中,也就是知足了Controller -> View 这个条件。
到此咱们就完成了MVC片断的组合,咱们从头再梳理一下这个过程
一、Is View,Activity实现View
将Activity赋予View的职责。
复制代码
二、Controller -> View ,在Activity中建立Controller的实例。
通知控制器Controller去处理一些事情
接收View的操做,并转调给Model
复制代码
三、View -> Controller,将View注入到Controller。
接收Controller,编辑本身与Model无关的状态
复制代码
四、Model -> View,在Activity中建立Model的实例。
主动询问Model的状态,呈现Model的数据
复制代码
五、View -> Model,将View注入到Model。
监听Model的变化,若有变化通知View
复制代码
六、Controller -> Model,将Contoller注入到View
监听Model的变化,若是变化通知Controller
复制代码
经过MVC的架构图,咱们尝试的设计了UML,而且经过Is a ,Has a原则肯定了View的归属,其次,经过依赖注入原则,肯定了依赖建立方式,再次,经过MVC的架构图与UML确立了Controller,View, Model这三者之间的关系,再结合依赖注入原则完成了整个框架的搭建。
在以前的介绍MVC各个层的职责中,“监听”这个词出现的频率很高,在面向对象语言的设计中,“监听”是一个动做,与它相反的即是“获取”这个动做了。目前这个框架仍是个雏形,并无“监听”的能力,同时包括各个层代码的不完善,以及控制反转等实际性内容的缺失…… 原本想用一章来写完,但篇幅有些长,看来不得不分了。之前总觉的看别人的帖子挺快的,不一下子就看完了,但真正本身去码但是真的慢,主要是担忧描述的不够清晰,担忧不能把本身的想法很清晰的表达出来,同时又担忧过于清晰的表达会不会又显得啰里八嗦,删了改,改了删的,总之,我会尽最大努力完成这个系列的,喜欢的就点个赞,鼓励鼓励我。
最后的最后,布置一个家庭做业,看下面三张图:
问题1,图一和图二的区别,以及图二算不算MVC 问题二、MVC能够像图三那样去设计吗?