為什么要先介紹MVC?
如果你要想更佳深刻的理解MVP,并在實(shí)際開(kāi)發(fā)中靈活的應(yīng)用,那么就要先了解它的低配版MVC,他倆只是一步之遙,先了解MVC再學(xué)習(xí)MVP,MVP的優(yōu)勢(shì)才能凸顯出來(lái),這樣連貫性的學(xué)習(xí)才會(huì)加深對(duì)MVP的理解。
目錄
MVP那些事兒(1)……用場(chǎng)景說(shuō)話
MVP那些事兒(5)……中介者模式與MVP的關(guān)系【知識(shí)點(diǎn)】
MVP那些事兒(6)……MVC變身為MVP
MVP那些事兒(7)……Kotlin實(shí)現(xiàn)MVP【知識(shí)點(diǎn)】
MVP那些事兒(8)……當(dāng)MVP遇到Lifecycle【知識(shí)點(diǎn)】
MVP那些事兒(9)……探究MVP的最佳實(shí)踐
MVP那些事兒(10)……MVVM雙向綁定
MVP那些事兒(11)……基于MVVM的Architecture Components
快速回顧
在上一篇中,我們學(xué)習(xí)了MVC架構(gòu)圖原理和它的進(jìn)化過(guò)程,并通過(guò)is a,has a,依賴注入原則對(duì)MVC中三個(gè)對(duì)象進(jìn)行組合,同時(shí)從無(wú)到有的搭建出MVC框架的基本雛形,
靈與骨,血與肉
在上一篇中,我們的MVC框架已經(jīng)完成了初步的搭建,當(dāng)然,還不是框架最終形態(tài),雖然三個(gè)對(duì)象通過(guò)某種聯(lián)系組合了起來(lái),但讓框架真正運(yùn)轉(zhuǎn)起來(lái)還需要最關(guān)鍵的一個(gè)機(jī)制,那就是溝通機(jī)制,就好比人類(lèi),光有骨架和血肉還不能稱(chēng)之為一個(gè)完整的“人”,你還需要神經(jīng)系統(tǒng)幫助你去看,聽(tīng),和感受。
溝通機(jī)制
在Java的面向?qū)ο笤O(shè)計(jì)中,監(jiān)聽(tīng)是一種常用的溝通機(jī)制,在觀察者模式里,一個(gè)監(jiān)聽(tīng)機(jī)制所涉及到的對(duì)象包括:監(jiān)聽(tīng)者(Observer)、被監(jiān)聽(tīng)者(Obserable);涉及到的環(huán)節(jié)包括:訂閱(Subscribe)、發(fā)送事件、及處理事件。
場(chǎng)景
使用以下兩個(gè)需求作為本章場(chǎng)景:
1、列表展示
2、列表支持下拉刷新,上拉加載更多
實(shí)現(xiàn)監(jiān)聽(tīng)機(jī)制
既然監(jiān)聽(tīng)是一個(gè)常用的溝通手段,我們就開(kāi)始“升級(jí)”我們的框架
監(jiān)聽(tīng)的好處
在開(kāi)始之前,依舊要用一個(gè)場(chǎng)景來(lái)描述一下監(jiān)聽(tīng)的好處,還記得之前租房子的故事嗎?在這個(gè)故事里,我故意忽略了溝通的機(jī)制,就是為了留在這一章節(jié)講的,當(dāng)租客聯(lián)系到中介時(shí),這是一個(gè)主動(dòng)的動(dòng)作,租客是發(fā)起方,當(dāng)和中介建立聯(lián)系后,他們雙方互留電話,同時(shí)中介找到合適的房東,并且也留下了聯(lián)系方式,這個(gè)時(shí)候中介開(kāi)始等待房東的回應(yīng),這期間中介什么都干不了,一分鐘一個(gè)電話的詢問(wèn)房東是否考慮好了,那么中介的下場(chǎng)只有兩個(gè),房東很生氣,一分鐘一個(gè)電話,你沒(méi)事兒,我還有事兒呢,你等我消息不行嗎?直接拉黑?;蛘哂捎谥薪橐淮沃荒芴幚硪粋€(gè)事情,這件事處理不完,就不能處理下一件事,效率低下被公司開(kāi)除。租客也是一樣,一次次的去詢問(wèn)中介,找到房子了嗎?等待他的下場(chǎng)也有兩個(gè),一、一次次的電話,導(dǎo)致電話費(fèi)報(bào)表,二、由于電話費(fèi)太貴,打算一天問(wèn)一次,由于獲取消息不及時(shí),結(jié)果房子被別人租走了,也就是消息的即時(shí)性低,而露宿街頭(雖朱門(mén)酒肉臭,但別路有凍死骨,愿在外漂泊的你們?cè)谶@寒冷的冬天里有一個(gè)溫暖的所在)。
為了避免上面的悲劇發(fā)生,中介公司改善了溝通機(jī)制,首先從租戶的角度,通過(guò)主動(dòng)向租客匯報(bào)進(jìn)度來(lái)解決消息即時(shí)性的問(wèn)題,讓租戶第一時(shí)間得到最新情況,其次,中介不再催促房東,而是讓房東考慮好后通知中介,當(dāng)中介收到房東的消息后第一時(shí)間通知給租戶,通過(guò)這兩個(gè)環(huán)節(jié)的改造,一條高效的通知鏈就形成了。
為MVC框架增加監(jiān)聽(tīng)
Modle的職責(zé)是對(duì)數(shù)據(jù)的生產(chǎn)和處理,并在結(jié)束一些耗時(shí)的操作后,應(yīng)該主動(dòng)的通知給Controller,所以Model為被觀察對(duì)象,而Controller為觀察對(duì)象,它觀察著Model的一舉一動(dòng),為了能更好的觀察Model的行為,Controller派了一個(gè)“眼線”到Model中,這個(gè)“眼線”的職責(zé)就是監(jiān)聽(tīng)Model的一舉一動(dòng)。
第一步,定義一個(gè)“眼線”
/**我是一個(gè)“眼線”
public interface Observer {}
這里的眼線就是一個(gè)觀察對(duì)象的接口,但具體讓它做什么,我們還不清楚,通過(guò)接口的形式未來(lái)會(huì)有很好的擴(kuò)展性,定義完眼線,如何使用呢?
還記得上一篇中View是被怎么使用的嗎?它被Actvity實(shí)現(xiàn)了,也即是說(shuō)我們這里的“眼線”也應(yīng)該被某個(gè)對(duì)象去實(shí)現(xiàn),否則它將沒(méi)有任何用處,由于是Controller派出了一個(gè)“眼線”,所以應(yīng)該由Controller去使用,使用的兩種途徑,要么自己具備“眼線”的功能,也就是is a,要么就是自己招募一個(gè)“眼線”,Has a。
1、我就是眼線,眼線就是我
/**我是一個(gè)Contorller,同時(shí)我就是個(gè)眼線**/
public class TasksController implements Observer{
void loadNomData() {}
}
TasksController,通過(guò)實(shí)現(xiàn)Observer接口,具備了觀察者的能力。
2、我招了個(gè)眼線
/**我是一個(gè)Contorller**/
public class TasksController{
//我招募了一名眼線
private Observer observer = new Observer() {};
void loadNomData() {}
}
TasksController,通過(guò)內(nèi)部實(shí)例化了一個(gè)Observer接口,間接的獲得了觀察者的能力。
以上兩種都可以獲得觀察者的能力,但是從擴(kuò)展性來(lái)講,還是盡量去選擇第一種方式。
第二步,放置眼線
有了眼線后,我們還要將它放置在被觀察者的內(nèi)部,這才算完成了觀察者與被觀察者之間的訂閱。
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,通過(guò)構(gòu)造器注入
TasksController controller =
new TasksController(tasksView:this);
//初始化Model,Model -> View and View -> Model
TasksRepository model =
new TasksRepository(tasksView:this);
//Controller -> Model
model.setController(controller);
}
}
這是上一篇內(nèi)容中的代碼段,未來(lái)都會(huì)圍繞著這段代碼進(jìn)行改進(jìn),看最下面這一行:
model.setController(controller);
其實(shí),這一步就是model持有了controller,由于我們現(xiàn)在的controller具備了觀察者的職責(zé),同時(shí)在我們真正的使用中沒(méi)有必要把整個(gè)controller的職責(zé)都暴露給model,而model也只需要controller觀察者的能力,好讓它即時(shí)的把結(jié)果告知controller,所以我們可以這樣改造一下這段代碼為:
model.addObserver(observer: controller);
看起來(lái)傳的參數(shù)依舊是controller,只不過(guò)改了一個(gè)方法名,這沒(méi)什么區(qū)別啊,我想說(shuō)的是區(qū)別還是有的,方法名的改變意味著這段代碼的業(yè)務(wù)變了,雖然都是controller,沒(méi)改之前是全部的controller,而下面的代碼是告訴大家,我只使用controller觀察者的部分,其他的我不關(guān)心,雖然你全給了我,但用那些是我的事情。
改造過(guò)后的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,通過(guò)構(gòu)造器注入
TasksController controller =
new TasksController(tasksView:this);
//初始化Model,Model -> View and View -> Model
TasksRepository model =
new TasksRepository(tasksView:this);
//Controller -> Model
model.addObserver(observer: controller);
}
}
第三步,發(fā)送事件
這個(gè)時(shí)候,Model已經(jīng)獲取到了觀察者,也就是Controller,那么當(dāng)Model自己發(fā)生變化時(shí),就可以即時(shí)的通知給Controller了,我們?cè)囍l(fā)一個(gè)事件,但是在發(fā)送事件前,不要忘了眼線還沒(méi)有具體的能力,我們只是定義了一個(gè)接口,眼線具體有什么能力還是要結(jié)合具體業(yè)務(wù)去定義,這不屬于架構(gòu)的部分,更偏向于業(yè)務(wù)層,這里我們就模擬當(dāng)Model獲取到數(shù)據(jù)后,通知Controller,我拿到數(shù)據(jù)了,所以讓眼線有通知數(shù)據(jù)ok的功能:
/**我是一個(gè)“眼線”
public interface Observer {
//數(shù)據(jù)OK
void onDataComplate(Data data);
}
目前眼線已經(jīng)準(zhǔn)備完畢,就等著Model來(lái)使用了,我們用Model來(lái)發(fā)送一個(gè)事件
Model :TasksRepository
/**我是一個(gè)Model**/
public class TasksRepository {
//眼線集中營(yíng)
public static ArrayList<Observer> observers =
new ArrayList<Observer>();
viod addObserver(Observer observer){
observers.add(observer);
}
//從服務(wù)器請(qǐng)求獲取數(shù)據(jù)
void getTasks() {
//訪問(wèn)服務(wù)器,耗時(shí)。。。服務(wù)器返回時(shí),
Data data = fromServer();
//發(fā)送事件
for(Observer observer : observers){
observer.onDataComplate(data);
}
}
//從內(nèi)存緩存獲取數(shù)據(jù)
Data getTaskCache() {}
//從磁盤(pán)緩存獲取數(shù)據(jù)
Data getTaskDiskCache(){}
//保存一條數(shù)據(jù)
boolean saveTask(Task task) {}
//對(duì)數(shù)據(jù)進(jìn)行排序
Data orderData(Data data, int orderType){}
}
在實(shí)際的開(kāi)發(fā)中,Model可不是只為了某一個(gè)Controller去監(jiān)聽(tīng)的,它可以被任何想要監(jiān)聽(tīng)它的人監(jiān)聽(tīng),你只要送一個(gè)眼線過(guò)來(lái),當(dāng)Modle有變動(dòng)時(shí),Model會(huì)通知所有關(guān)心它的人,所以Model里面有一個(gè)Observer的集合:
public ArrayList<Observer> observers =
new ArrayList<Observer>();
當(dāng)Model發(fā)生了變化,就會(huì)遍歷這個(gè)集合去通知所有的觀察者,而眼線在這里派上了用場(chǎng)
for(Observer observer : observers){
observer.onDataComplate(data);
}
第四步,接收事件
處理事件的特性是觀察者的本質(zhì),Controller既然是觀察者,那么處理事件應(yīng)該由自己去完成:
Controller :TasksController
/**我是一個(gè)Contorller**/
public class TasksController implements Observer{
//接收事件
void onDataComplate(Data data) {
//處理事件
}
void loadNomData() {}
}
TasksController實(shí)現(xiàn)了Observer的onDataComplate方法,當(dāng)Model發(fā)送事件后,onDataComplate方法便能接收到,我們就可以在這里處理事件了,到此為止整個(gè)事件從創(chuàng)建到處理就完成了,這也就是觀察者模式的核心,如果以后需要自己實(shí)現(xiàn)一個(gè)觀察者模式,那么就按照上面四個(gè)步驟來(lái)寫(xiě),絕對(duì)不會(huì)懵圈而且思路會(huì)異常的清晰。
那么View呢?
上面的場(chǎng)景提到過(guò),當(dāng)房東考慮好后通知給中介,中介會(huì)第一時(shí)間通知租客結(jié)果,那么具體改如何做呢?
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,通過(guò)構(gòu)造器注入
TasksController controller =
new TasksController(tasksView:this);
//初始化Model,Model -> View and View -> Model
TasksRepository model =
new TasksRepository(tasksView:this);
//Controller -> Model
model.addObserver(observer: controller);
}
}
回過(guò)頭來(lái)看Acivity的代碼中的這一段:
//初始化Controller,this就是View,通過(guò)構(gòu)造器注入
TasksController controller =
new TasksController(tasksView:this);
首先,通過(guò)構(gòu)造函數(shù),讓controller持有view,當(dāng)controller接收到model的通知時(shí),緊接著通知view,所以TasksController的代碼還需改進(jìn):
/**我是一個(gè)Contorller**/
public class TasksController implements Observer{
//通過(guò)構(gòu)造函數(shù)接收view
public TasksController(TasksView view) {
this.view = view;
}
//接收事件
void onDataComplate(Data data) {
//處理事件,緊接著向view發(fā)送事件
view.onDataBack(data);
}
void loadNomData() {}
}
我們看處理事件的部分,直接執(zhí)行了view的方法,也就是所謂的即刻通知。
讓View也接口化
按早之前Controller觀察者化的思路,我們能不能讓view也變成觀察者,當(dāng)然可以而且是必須的,讓view 去觀察Controller的變化,Controller又去觀察Model的變化,那么整個(gè)鏈?zhǔn)椒磻?yīng)就完成了。具體步驟就不分析了,上一個(gè)完整的代碼:
View :TasksView
/**我是一個(gè)View,我本身就是個(gè)眼線**/
public interface TaskView {
void onDataBack(Data);
//當(dāng)列表初始化后,告訴控制器該加載數(shù)據(jù)了
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,通過(guò)構(gòu)造器注入
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){
//處理事件。。。
}
//當(dāng)列表初始化后,告訴控制器該加載數(shù)據(jù)了
void viewCreate(){
controller.loadNomData();
}
//更新列表
void upDateList(){}
//just for ui
void beginLoadData(){}
}
總結(jié):
這一篇中,我們通過(guò)觀察者模式對(duì)我們的框架進(jìn)行了改進(jìn),通過(guò)監(jiān)聽(tīng),讓MVC的三個(gè)對(duì)象形成了一個(gè)事件傳送帶,事件就好比有了方向一般從Model出發(fā),經(jīng)過(guò)Controller最終流向View,而后期我們可以在這條鏈路上對(duì)我們的事件做任何想要做的操作,而最終的接收者View是完全不用關(guān)心的,亦或者view可以自定義自己想要的數(shù)據(jù),在Model還沒(méi)有發(fā)送事件前。說(shuō)的更確切點(diǎn),我們可以在事件的發(fā)送前,傳輸中,接收前,這三個(gè)點(diǎn)做很多我們希望做的事情,比如數(shù)據(jù)在接收前的一些排序的轉(zhuǎn)變,這些我們都會(huì)以接口的 方式暴露出來(lái)。到此,MVC的介紹結(jié)束,但框架的搭建還沒(méi)有完成,在接下來(lái)的被容里,我們通過(guò)MVP的方式對(duì)框架進(jìn)行進(jìn)一步的改進(jìn),同時(shí)加入一些實(shí)質(zhì)些的工具,讓框架具備一些基本的業(yè)務(wù)功能。