为什么要先介绍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
快速回顾
在上一篇中,我们学习了MVC架构图原理和它的进化过程,并通过is a,has a,依赖注入原则对MVC中三个对象进行组合,同时从无到有的搭建出MVC框架的基本雏形,
灵与骨,血与肉
在上一篇中,我们的MVC框架已经完成了初步的搭建,当然,还不是框架最终形态,虽然三个对象通过某种联系组合了起来,但让框架真正运转起来还需要最关键的一个机制,那就是沟通机制,就好比人类,光有骨架和血肉还不能称之为一个完整的“人”,你还需要神经系统帮助你去看,听,和感受。
沟通机制
在Java的面向对象设计中,监听是一种常用的沟通机制,在观察者模式里,一个监听机制所涉及到的对象包括:监听者(Observer)、被监听者(Obserable);涉及到的环节包括:订阅(Subscribe)、发送事件、及处理事件。
场景
使用以下两个需求作为本章场景:
1、列表展示
2、列表支持下拉刷新,上拉加载更多
实现监听机制
既然监听是一个常用的沟通手段,我们就开始“升级”我们的框架
监听的好处
在开始之前,依旧要用一个场景来描述一下监听的好处,还记得之前租房子的故事吗?在这个故事里,我故意忽略了沟通的机制,就是为了留在这一章节讲的,当租客联系到中介时,这是一个主动的动作,租客是发起方,当和中介建立联系后,他们双方互留电话,同时中介找到合适的房东,并且也留下了联系方式,这个时候中介开始等待房东的回应,这期间中介什么都干不了,一分钟一个电话的询问房东是否考虑好了,那么中介的下场只有两个,房东很生气,一分钟一个电话,你没事儿,我还有事儿呢,你等我消息不行吗?直接拉黑。或者由于中介一次只能处理一个事情,这件事处理不完,就不能处理下一件事,效率低下被公司开除。租客也是一样,一次次的去询问中介,找到房子了吗?等待他的下场也有两个,一、一次次的电话,导致电话费报表,二、由于电话费太贵,打算一天问一次,由于获取消息不及时,结果房子被别人租走了,也就是消息的即时性低,而露宿街头(虽朱门酒肉臭,但别路有冻死骨,愿在外漂泊的你们在这寒冷的冬天里有一个温暖的所在)。
为了避免上面的悲剧发生,中介公司改善了沟通机制,首先从租户的角度,通过主动向租客汇报进度来解决消息即时性的问题,让租户第一时间得到最新情况,其次,中介不再催促房东,而是让房东考虑好后通知中介,当中介收到房东的消息后第一时间通知给租户,通过这两个环节的改造,一条高效的通知链就形成了。
为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的方式对框架进行进一步的改进,同时加入一些实质些的工具,让框架具备一些基本的业务功能。
网友评论