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

1,517 阅读11分钟

为什么要先介绍MVC?

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

目录

MVP那些事儿(1)……用场景说话

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

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

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

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

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

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

快速回顾

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

在上一篇中,我们学习了MVC架构图原理和它的进化过程,并通过is a,has a,依赖注入原则对MVC中三个对象进行组合,同时从无到有的搭建出MVC框架的基本雏形,

灵与骨,血与肉

在上一篇中,我们的MVC框架已经完成了初步的搭建,当然,还不是框架最终形态,虽然三个对象通过某种联系组合了起来,但让框架真正运转起来还需要最关键的一个机制,那就是沟通机制,就好比人类,光有骨架和血肉还不能称之为一个完整的“人”,你还需要神经系统帮助你去看,听,和感受。

沟通机制

在Java的面向对象设计中,监听是一种常用的沟通机制,在观察者模式里,一个监听机制所涉及到的对象包括:监听者(Observer)、被监听者(Obserable);涉及到的环节包括:订阅(Subscribe)、发送事件、及处理事件。

实现监听机制

既然监听是一个常用的沟通手段,我们就开始“升级”我们的框架

监听的好处

在开始之前,依旧要用一个场景来描述一下监听的好处,还记得之前租房子的故事吗?在这个故事里,我故意忽略了沟通的机制,就是为了留在这一章节讲的,当租客联系到中介时,这是一个主动的动作,租客是发起方,当和中介建立联系后,他们双方互留电话,同时中介找到合适的房东,并且也留下了联系方式,这个时候中介开始等待房东的回应,这期间中介什么都干不了,一分钟一个电话的询问房东是否考虑好了,那么中介的下场只有两个,房东很生气,一分钟一个电话,你没事儿,我还有事儿呢,你等我消息不行吗?直接拉黑。或者由于中介一次只能处理一个事情,这件事处理不完,就不能处理下一件事,效率低下被公司开除。租客也是一样,一次次的去询问中介,找到房子了吗?等待他的下场也有两个,一、频繁的拨打电话,造城高昂的资源浪费,二、由于电话费太贵,打算一天问一次,可由于获取消息不及时,结果房子被别人租走了,也就是消息的即时性低。

为了避免上面的悲剧发生,中介公司改善了沟通机制,首先从租户的角度,通过主动向租客汇报进度来解决消息即时性的问题,让租户第一时间得到最新情况,其次,中介不再催促房东,而是让房东考虑好后通知中介,当中介收到房东的消息后第一时间通知给租户,通过这两个环节的改造,一条高效的通知链就形成了。

为MVC框架增加监听

Modle的职责是对数据的生产和处理,并在结束一些耗时的操作后,应该主动的通知给Controller,所以Model为被观察对象,而Controller为观察对象,它观察着Model的一举一动,为了能更好的观察Model的行为,Controller派了一个“眼线”到Model中,这个“眼线”的职责就是监听Model的一举一动。

第一步,定义一个“眼线”

/**我是一个“眼线”
public interface Observer {}

这里的眼线就是一个观察对象的接口,但具体让它做什么,我们还不清楚,通过接口的形式未来会有很好的扩展性,定义完眼线,如何使用呢?

还记得上一篇中View是被怎么使用的吗?它被Actvity实现了,也即是说我们这里的“眼线”也应该被某个对象去实现,否则它将没有任何用处,由于是Controller派出了一个“眼线”,所以应该由Controller去使用,使用的两种途径,要么自己具备“眼线”的功能,也就是is a,要么就是自己招募一个“眼线”,Has a。

1、我就是眼线,眼线就是我

/**我是一个Contorller,同时我就是个眼线**/
public class TasksController implements Observer{
    void loadNomData() {}
}

TasksController,通过实现Observer接口,具备了观察者的能力。

2、我招了个眼线

/**我是一个Contorller**/
public class TasksController{
    //我招募了一名眼线
    private Observer observer = new Observer() {};
    void loadNomData() {}
}

TasksController,通过内部实例化了一个Observer接口,间接的获得了观察者的能力。

以上两种都可以获得观察者的能力,但是从扩展性来讲,还是尽量去选择第一种方式。

第二步,放置眼线

有了眼线后,我们还要将它放置在被观察者的内部,这才算完成了观察者与被观察者之间的订阅。

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);
    }
}

这是上一篇内容中的代码段,未来都会围绕着这段代码进行改进,看最下面这一行:

model.setController(controller);

其实,这一步就是model持有了controller,由于我们现在的controller具备了观察者的职责,同时在我们真正的使用中没有必要把整个controller的职责都暴露给model,而model也只需要controller观察者的能力,好让它即时的把结果告知controller,所以我们可以这样改造一下这段代码为:

model.addObserver(observer: controller);

看起来传的参数依旧是controller,只不过改了一个方法名,这没什么区别啊,我想说的是区别还是有的,方法名的改变意味着这段代码的业务变了,虽然都是controller,没改之前是全部的controller,而下面的代码是告诉大家,我只使用controller观察者的部分,其他的我不关心,虽然你全给了我,但用那些是我的事情。

改造过后的Activity:

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.addObserver(observer: controller);
    }
}

第三步,发送事件

这个时候,Model已经获取到了观察者,也就是Controller,那么当Model自己发生变化时,就可以即时的通知给Controller了,我们试着发一个事件,但是在发送事件前,不要忘了眼线还没有具体的能力,我们只是定义了一个接口,眼线具体有什么能力还是要结合具体业务去定义,这不属于架构的部分,更偏向于业务层,这里我们就模拟当Model获取到数据后,通知Controller,我拿到数据了,所以让眼线有通知数据ok的功能:

/**我是一个“眼线”
public interface Observer {
    //数据OK
    void onDataComplate(Data data);
}

目前眼线已经准备完毕,就等着Model来使用了,我们用Model来发送一个事件

Model :TasksRepository

/**我是一个Model**/
public class TasksRepository {
    //眼线集中营
    public static ArrayList<Observer> observers = 
        new ArrayList<Observer>();
    viod addObserver(Observer observer){
        observers.add(observer);
    }
    //从服务器请求获取数据
    void getTasks() {
        //访问服务器,耗时。。。服务器返回时,
        Data data = fromServer();
        //发送事件
        for(Observer observer : observers){
            observer.onDataComplate(data);
        }
    }
    //从内存缓存获取数据
    Data getTaskCache() {}
    //从磁盘缓存获取数据
    Data getTaskDiskCache(){}
    //保存一条数据
    boolean saveTask(Task task) {}
    //对数据进行排序
    Data orderData(Data data, int orderType){}
}


在实际的开发中,Model可不是只为了某一个Controller去监听的,它可以被任何想要监听它的人监听,你只要送一个眼线过来,当Modle有变动时,Model会通知所有关心它的人,所以Model里面有一个Observer的集合:

public ArrayList<Observer> observers = 
            new ArrayList<Observer>();

当Model发生了变化,就会遍历这个集合去通知所有的观察者,而眼线在这里派上了用场

for(Observer observer : observers){
    observer.onDataComplate(data);
}

第四步,接收事件

处理事件的特性是观察者的本质,Controller既然是观察者,那么处理事件应该由自己去完成:

Controller :TasksController

/**我是一个Contorller**/
public class TasksController implements Observer{
    //接收事件
    void onDataComplate(Data data) {
        //处理事件
    }
    void loadNomData() {}
}

TasksController实现了Observer的onDataComplate方法,当Model发送事件后,onDataComplate方法便能接收到,我们就可以在这里处理事件了,到此为止整个事件从创建到处理就完成了,这也就是观察者模式的核心,如果以后需要自己实现一个观察者模式,那么就按照上面四个步骤来写,绝对不会懵圈而且思路会异常的清晰。

那么View呢?

上面的场景提到过,当房东考虑好后通知给中介,中介会第一时间通知租客结果,那么具体改如何做呢?

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.addObserver(observer: controller);
    }   
}

回过头来看Acivity的代码中的这一段:

//初始化Controller,this就是View,通过构造器注入
TasksController controller = 
            new TasksController(tasksView:this);

首先,通过构造函数,让controller持有view,当controller接收到model的通知时,紧接着通知view,所以TasksController的代码还需改进:

/**我是一个Contorller**/
public class TasksController implements Observer{
    //通过构造函数接收view
    public TasksController(TasksView view) {
        this.view = view;
    }
    //接收事件
    void onDataComplate(Data data) {
        //处理事件,紧接着向view发送事件
        view.onDataBack(data);
    }
    void loadNomData() {}
}

我们看处理事件的部分,直接执行了view的方法,也就是所谓的即刻通知。

让View也接口化,这是很关键的一步

按早之前Controller观察者化的思路,我们能不能让view也变成观察者,当然可以而且是必须的,让view 去观察Controller的变化,Controller又去观察Model的变化,那么整个链式反应就完成了。看如下相对完整的示例代码:

View :TasksView

/**我是一个View,我本身就是个眼线**/
public interface TaskView {
    void onDataBack(Data);
    //当列表初始化后,告诉控制器该加载数据了
    void viewCreate();
    //更新列表
    void upDateList();
    //just for ui
    void beginLoadData();
}

Activity:

public class MainActivity
extends AppCompatActivity
implments TasksView{
    private TasksController controller;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化Controller,this就是View,通过构造器注入
        controller = 
            new TasksController(tasksView:this);
        
        //初始化Model,Model -> View and View -> Model
        TasksRepository model = 
            new TasksRepository(tasksView:this);
        
        //Controller -> Model
        model.addObserver(observer: controller);
        viewCreate();
    }
    //接收controller的事件,并处理
    void onDataBack(Data){
        //处理事件。。。
    }
    //当列表初始化后,告诉控制器该加载数据了
    void viewCreate(){
        controller.loadNomData();
    }
    //更新列表
    void upDateList(){}
    //just for ui
    void beginLoadData(){}
}

总结:

这一篇中,我们通过观察者模式对我们的框架进行了改进,通过监听,让MVC的三个对象形成了一个事件传送带,事件就好比有了方向一般从Model出发,经过Controller最终流向View,而后期我们可以在这条链路上对我们的事件做任何想要做的操作,而最终的接收者View是完全不用关心的,亦或者view可以自定义自己想要的数据,在Model还没有发送事件前。说的更确切点,我们可以在事件的发送前,传输中,接收前,这三个点做很多我们希望做的事情,比如数据在接收前的一些排序的转变,这些我们都会以接口的 方式暴露出来。到此,MVC的介绍结束,但框架的搭建还没有完成,在接下来的内容里,我们通过MVP的方式对框架进行进一步的改进,同时加入一些实质些的工具,让框架具备一些基本的业务功能。