MVP那些事儿 (3)……在Android中使用MVC(上)

目录

MVP那些事儿(1)……用场景说话设计模式

MVP那些事儿(2)……MVC架构初探缓存

MVP那些事儿(3)……在Android中使用MVC(上)服务器

MVP那些事儿(4)……在Android中使用MVC(下)架构

MVP那些事儿(5)……中介者模式与MVP的关系框架

MVP那些事儿(6)……MVC变身为MVPide

MVP那些事儿(7)……Repository设计分析函数

为何要先介绍MVC?

若是你要想更佳深入的理解MVP,并在实际开发中灵活的应用,那么就要先了解它的低配版MVC,他俩只是一步之遥,先了解MVC再学习MVP,MVP的优点才能凸显出来,这样连贯性的学习才会加深对MVP的理解。布局

回顾MVP那些事儿(2) 初探MVC架构的内容:post

MVC的做用?它是设计模式吗?是框架吗?是架构吗? MVC各个层的定义和职责学习

经过上一篇的内容,你们也对MVC已经有了一个大体的了解,在开启这一章内容前,但愿你们能先阅读上一篇的内容,不然可能会“断片”。 在上一篇中,咱们详细的介绍了MVC三个层的基本职责,咱们再回顾一下这三个“片断”:

Model层

一、JavaBean不是Model,但Model也能够包含JavaBean的职责,但不是必须的。 二、Model是用来处理数据的,如获取数据(本地或者服务端),数据处理,如CURD。

View层

一、它的主要职责为呈现Model的数据、主动询问状态或被动的监听 二、通知控制器Controller去处理一些事情 三、接收Controller,编辑本身与Model无关的状态

Controller

一、接收View的操做,并转调给Model 二、改变View的状态

同时还遗留了两个问题:

一、如何把这三个片断组装起来?在Android里怎么写? 二、view在执行tasksRepository.getTaskCache()时,是怎么知道tasksRepository数据已经准备好了呢?怎么通知view的?

把片断“组合”起来

在模式设计时,咱们会经过关系图来反映对象间的关系,以下图:

MVC架构图

这是一张很是经典的MVC架构图(手残,有点丑……),相信你们对这张图已经不陌生了,OK,这张图我先放下不表,由于考虑到咱们介绍的是架构,自己是抽象的,加上如此抽象的图,怕是不能好好的切入,因此我想尽量的巨像化这个过程,当你们有一个清晰的理解后,再回过头来从新审视它。我原本是想放在文章的后半段祭出这张图的,但想了想有点不妥,缘由是咱们上期已经经过一整篇用来初探MVC,也该把MVC的架构图贴出来应一应景了……

那么MVC这三者的关系到底是怎样的呢?

咱们依旧经过现实中的场景来描述一下MVC三者的关系,让你们对这三者的关系有一个初期的直观的理解,起到一个抛砖引玉的效果,废话很少,场景开始。

场景

想必你们都租过房子,有当租客的体验,也有一部分人可能还有当过过房东经验,在当下的社会中,房屋中介是咱们租房找房必不可少的环节,我相信大部分人都和中介打过交道,咳咳,那咱们就还原一下租房的过程,

首先,肯定角色

在这个场景里包含了三个角色,租客、中介、和房东,

其次,选择角度

咱们从租户的角度描述一下租房的场景,只考虑正向流程。

再次,场景描述

租客要先找到中介并描述本身须要的房子,中介接到需求后,经过筛选发现有一个房东很适合,因而中介开始与这个房东接触,询问一些和房子租金相关的事宜,若是中介觉的合适,他会把反馈告知客户,若是客户也觉的合适,中介就会把租客和房东约在店里进行更进一步的协商,若是能促成合同,那么到此为止中介完成了现阶段的工做,剩下的事情,好比租客向房东每一个季度缴纳房租,或者询问房东家里电器的使用方式,亦或者房东询问房客租约到期是否续租等等,这些沟通均可以抛开中介去完成,也就是说,不必定须要中介做为他们之间沟通的桥梁,但也并不意味着中介在之后的场景中是不可用的,好比,在租房过程当中有一些事情租客和房东有分歧,也能够经过中介来进行协调和沟通。

还有一种中介的形式是,租客和房东是见不到彼此的,全程由中介负责,这种房叫托管房,租客不知道房东是谁,房东也不知道租客是谁,合同都和中介签,咱们如今不讨论这种状况。

来个图描述一下上诉的情景

好随便的图

这张图的目的一来是复述一下上面的场景描述,二来是让你们直观的看到它们三者交互的一个关系。

中介、租客、房东,把他们和MVC的三个角色关联起来

经过上诉场景的描述,和前一篇帖子中介绍的MVC各个对象的职责,想必他们到底谁是谁一目了然,咱们试着关联一下这几个对象

首先看中介,他负责了执行租客的招租要求,并告知房东这些要求,也就是说他做为中介向房东转告租客的需求,那中介就是咱们的Controller。

再次看租客,他是需求的发出者,他比其余几个对象更积极的提出需求,就比如View,老是再向Controller提出需求,也会更主动的向Model询问状态,就像租客同样,遇到问题,要么找中介,要么去询问房东,因此,在这里把租客看做是View更加的贴切。

最后只剩下房东这个角色了,最后的Model就分配给房东吧。

接下来咱们用MVC的方式来梳理一下中介、租客、房东的关系

一、View To Controller 求租房

二、Controller To Model 询问:出租吗?
三、Controller To View 告知:找到房源
四、View To Model 单聊:能便宜吗?
五、Model To View 单聊:没门儿!

上面这五张图,是把MVC三个对象间的关系,以及案例中的业务流程作了一个“融合”,是一个抽象到具象的“融合”,架构图中这些带箭头的线,是用来解释对象间关系的,无非就是那些谁依赖谁的解释,我同时又在线上加入了一些事件,是但愿能把上面具象的问题引到抽象的架构中来,在实际中,这样的融合是不符合常理的,好比你没法用单个业务场景去定义对象间的依赖关系,好比View To Controller,难道只有求租房这个场景才能够这么用吗?显然不是的,包括各个角色中括号后面的称呼。因此,为了解决通用性的问题,人们把这样的具象问题抽象成了一种依赖关系。

架构是蓝图,抽象的存在,而框架是具象的,它是为了解决具体某一类问题而设计的。

MVC架构图是怎么来的?抛去具象,看本质

具象到抽象三连图
抽象到最后就是咱们开篇的第一张图。

在某些场合里,须要咱们去介绍咱们的软件架构,这就须要咱们具有描述架构的能力,我觉的能够先描述一下具象的问题,再描述向上抽离的步骤,最后陈述抽象的产物,也就是咱们的架构,是一个不错的思路。

应用到实际中

经过上面大篇幅的抛砖引玉,相信你们对MVC的架构有了一个初步的认识,那么接下来咱们开始真正的使用,还记得上一篇咱们使用了一个需求:

需求:列表加载数据而且展现

咱们依旧使用这个需求,由于前面定义好了三个层的类,接下来要作的是按照MVC的架构把他们拼装起来:

在Android中使用

开始加入到实际开发阶段,咱们先建立一个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中,这些线和箭头即是记录着对象间的关系:

按照架构图的提示,咱们为TasksRepository、TasksView、TasksController这三个类梳理了一个大体的依赖关系,同时MainActivity被撂到了一边,并非它和这三个对象没有关系,而是它如今还不能直接用,它须要变一下,为何这么说呢,来看例子:

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 与 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

同时更新一下代码,咱们让MainActivity实现一个TasksView,赋予它View的职责

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的初始化

那么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能够像图三那样去设计吗?

相关文章
相关标签/搜索