為什么要先介紹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ō)話(huà)
MVP那些事兒(3)……在A(yíng)ndroid中使用MVC(上)
MVP那些事兒(4)……在A(yíng)ndroid中使用MVC(下)
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
快速回顧
MVP那些事兒(3)……在A(yíng)ndroid中使用MVC(上)
在上一篇中蜻懦,我們學(xué)習(xí)了MVC架構(gòu)圖原理和它的進(jìn)化過(guò)程裙椭,并通過(guò)is a弄跌,has a凶硅,依賴(lài)注入原則對(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ī)制乡括,在觀(guān)察者模式里,一個(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)系后辩涝,他們雙方互留電話(huà),同時(shí)中介找到合適的房東勘天,并且也留下了聯(lián)系方式怔揩,這個(gè)時(shí)候中介開(kāi)始等待房東的回應(yīng),這期間中介什么都干不了脯丝,一分鐘一個(gè)電話(huà)的詢(xún)問(wèn)房東是否考慮好了商膊,那么中介的下場(chǎng)只有兩個(gè),房東很生氣宠进,一分鐘一個(gè)電話(huà)翘狱,你沒(méi)事兒,我還有事兒呢砰苍,你等我消息不行嗎潦匈?直接拉黑阱高。或者由于中介一次只能處理一個(gè)事情茬缩,這件事處理不完赤惊,就不能處理下一件事,效率低下被公司開(kāi)除凰锡。租客也是一樣未舟,一次次的去詢(xún)問(wèn)中介,找到房子了嗎掂为?等待他的下場(chǎng)也有兩個(gè)裕膀,一、一次次的電話(huà)勇哗,導(dǎo)致電話(huà)費(fèi)報(bào)表昼扛,二、由于電話(huà)費(fèi)太貴欲诺,打算一天問(wèn)一次抄谐,由于獲取消息不及時(shí),結(jié)果房子被別人租走了扰法,也就是消息的即時(shí)性低蛹含,而露宿街頭(雖朱門(mén)酒肉臭,但別路有凍死骨塞颁,愿在外漂泊的你們?cè)谶@寒冷的冬天里有一個(gè)溫暖的所在)浦箱。
為了避免上面的悲劇發(fā)生,中介公司改善了溝通機(jī)制祠锣,首先從租戶(hù)的角度憎茂,通過(guò)主動(dòng)向租客匯報(bào)進(jìn)度來(lái)解決消息即時(shí)性的問(wèn)題,讓租戶(hù)第一時(shí)間得到最新情況锤岸,其次竖幔,中介不再催促房東,而是讓房東考慮好后通知中介是偷,當(dāng)中介收到房東的消息后第一時(shí)間通知給租戶(hù)拳氢,通過(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為被觀(guān)察對(duì)象留特,而Controller為觀(guān)察對(duì)象,它觀(guān)察著Model的一舉一動(dòng),為了能更好的觀(guān)察Model的行為蜕青,Controller派了一個(gè)“眼線(xiàn)”到Model中苟蹈,這個(gè)“眼線(xiàn)”的職責(zé)就是監(jiān)聽(tīng)Model的一舉一動(dòng)。
第一步右核,定義一個(gè)“眼線(xiàn)”
/**我是一個(gè)“眼線(xiàn)”
public interface Observer {}
這里的眼線(xiàn)就是一個(gè)觀(guān)察對(duì)象的接口慧脱,但具體讓它做什么,我們還不清楚贺喝,通過(guò)接口的形式未來(lái)會(huì)有很好的擴(kuò)展性菱鸥,定義完眼線(xiàn),如何使用呢躏鱼?
還記得上一篇中View是被怎么使用的嗎氮采?它被Actvity實(shí)現(xiàn)了,也即是說(shuō)我們這里的“眼線(xiàn)”也應(yīng)該被某個(gè)對(duì)象去實(shí)現(xiàn)染苛,否則它將沒(méi)有任何用處鹊漠,由于是Controller派出了一個(gè)“眼線(xiàn)”,所以應(yīng)該由Controller去使用殖侵,使用的兩種途徑,要么自己具備“眼線(xiàn)”的功能镰烧,也就是is a拢军,要么就是自己招募一個(gè)“眼線(xiàn)”,Has a怔鳖。
1茉唉、我就是眼線(xiàn),眼線(xiàn)就是我
/**我是一個(gè)Contorller结执,同時(shí)我就是個(gè)眼線(xiàn)**/
public class TasksController implements Observer{
void loadNomData() {}
}
TasksController度陆,通過(guò)實(shí)現(xiàn)Observer接口,具備了觀(guān)察者的能力献幔。
2懂傀、我招了個(gè)眼線(xiàn)
/**我是一個(gè)Contorller**/
public class TasksController{
//我招募了一名眼線(xiàn)
private Observer observer = new Observer() {};
void loadNomData() {}
}
TasksController,通過(guò)內(nèi)部實(shí)例化了一個(gè)Observer接口蜡感,間接的獲得了觀(guān)察者的能力蹬蚁。
以上兩種都可以獲得觀(guān)察者的能力,但是從擴(kuò)展性來(lái)講郑兴,還是盡量去選擇第一種方式犀斋。
第二步,放置眼線(xiàn)
有了眼線(xiàn)后情连,我們還要將它放置在被觀(guān)察者的內(nèi)部叽粹,這才算完成了觀(guān)察者與被觀(guān)察者之間的訂閱。
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具備了觀(guān)察者的職責(zé),同時(shí)在我們真正的使用中沒(méi)有必要把整個(gè)controller的職責(zé)都暴露給model每强,而model也只需要controller觀(guān)察者的能力始腾,好讓它即時(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ā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)獲取到了觀(guān)察者攘轩,也就是Controller,那么當(dāng)Model自己發(fā)生變化時(shí)码俩,就可以即時(shí)的通知給Controller了度帮,我們?cè)囍l(fā)一個(gè)事件,但是在發(fā)送事件前稿存,不要忘了眼線(xiàn)還沒(méi)有具體的能力够傍,我們只是定義了一個(gè)接口,眼線(xiàn)具體有什么能力還是要結(jié)合具體業(yè)務(wù)去定義挠铲,這不屬于架構(gòu)的部分冕屯,更偏向于業(yè)務(wù)層,這里我們就模擬當(dāng)Model獲取到數(shù)據(jù)后拂苹,通知Controller安聘,我拿到數(shù)據(jù)了痰洒,所以讓眼線(xiàn)有通知數(shù)據(jù)ok的功能:
/**我是一個(gè)“眼線(xiàn)”
public interface Observer {
//數(shù)據(jù)OK
void onDataComplate(Data data);
}
目前眼線(xiàn)已經(jīng)準(zhǔn)備完畢,就等著Model來(lái)使用了浴韭,我們用Model來(lái)發(fā)送一個(gè)事件
Model :TasksRepository
/**我是一個(gè)Model**/
public class TasksRepository {
//眼線(xiàn)集中營(yíng)
public static ArrayList<Observer> observers =
new ArrayList<Observer>();
viod addObserver(Observer observer){
observers.add(observer);
}
//從服務(wù)器請(qǐng)求獲取數(shù)據(jù)
void getTasks() {
//訪(fǎng)問(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è)眼線(xiàn)過(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è)集合去通知所有的觀(guān)察者哈误,而眼線(xiàn)在這里派上了用場(chǎng)
for(Observer observer : observers){
observer.onDataComplate(data);
}
第四步,接收事件
處理事件的特性是觀(guān)察者的本質(zhì)躏嚎,Controller既然是觀(guān)察者蜜自,那么處理事件應(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)建到處理就完成了珠漂,這也就是觀(guān)察者模式的核心晚缩,如果以后需要自己實(shí)現(xiàn)一個(gè)觀(guān)察者模式尾膊,那么就按照上面四個(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觀(guān)察者化的思路命浴,我們能不能讓view也變成觀(guān)察者,當(dāng)然可以而且是必須的贱除,讓view 去觀(guān)察Controller的變化生闲,Controller又去觀(guān)察Model的變化,那么整個(gè)鏈?zhǔn)椒磻?yīng)就完成了勘伺。具體步驟就不分析了跪腹,上一個(gè)完整的代碼:
View :TasksView
/**我是一個(gè)View,我本身就是個(gè)眼線(xiàn)**/
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ò)觀(guān)察者模式對(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ù)功能描睦。