MVP那些事兒(3)……在Android中使用MVC(上)服務器
MVP那些事兒(4)……在Android中使用MVC(下)架構
若是你要想更佳深入的理解MVP,並在實際開發中靈活的應用,那麼就要先了解它的低配版MVC,他倆只是一步之遙,先了解MVC再學習MVP,MVP的優點才能凸顯出來,這樣連貫性的學習纔會加深對MVP的理解。佈局
回顧MVP那些事兒(2) 初探MVC架構的內容:post
MVC的做用?它是設計模式嗎?是框架嗎?是架構嗎? MVC各個層的定義和職責學習
經過上一篇的內容,你們也對MVC已經有了一個大體的瞭解,在開啓這一章內容前,但願你們能先閱讀上一篇的內容,不然可能會「斷片」。 在上一篇中,咱們詳細的介紹了MVC三個層的基本職責,咱們再回顧一下這三個「片斷」:
一、JavaBean不是Model,但Model也能夠包含JavaBean的職責,但不是必須的。 二、Model是用來處理數據的,如獲取數據(本地或者服務端),數據處理,如CURD。
一、它的主要職責爲呈現Model的數據、主動詢問狀態或被動的監聽 二、通知控制器Controller去處理一些事情 三、接收Controller,編輯本身與Model無關的狀態
一、接收View的操做,並轉調給Model 二、改變View的狀態
同時還遺留了兩個問題:
一、如何把這三個片斷組裝起來?在Android裏怎麼寫? 二、view在執行tasksRepository.getTaskCache()時,是怎麼知道tasksRepository數據已經準備好了呢?怎麼通知view的?
在模式設計時,咱們會經過關係圖來反映對象間的關係,以下圖:
這是一張很是經典的MVC架構圖(手殘,有點醜……),相信你們對這張圖已經不陌生了,OK,這張圖我先放下不表,由於考慮到咱們介紹的是架構,自己是抽象的,加上如此抽象的圖,怕是不能好好的切入,因此我想盡量的巨像化這個過程,當你們有一個清晰的理解後,再回過頭來從新審視它。我原本是想放在文章的後半段祭出這張圖的,但想了想有點不妥,緣由是咱們上期已經經過一整篇用來初探MVC,也該把MVC的架構圖貼出來應一應景了……
咱們依舊經過現實中的場景來描述一下MVC三者的關係,讓你們對這三者的關係有一個初期的直觀的理解,起到一個拋磚引玉的效果,廢話很少,場景開始。
想必你們都租過房子,有當租客的體驗,也有一部分人可能還有當過過房東經驗,在當下的社會中,房屋中介是咱們租房找房必不可少的環節,我相信大部分人都和中介打過交道,咳咳,那咱們就還原一下租房的過程,
在這個場景裏包含了三個角色,租客、中介、和房東,
咱們從租戶的角度描述一下租房的場景,只考慮正向流程。
租客要先找到中介並描述本身須要的房子,中介接到需求後,經過篩選發現有一個房東很適合,因而中介開始與這個房東接觸,詢問一些和房子租金相關的事宜,若是中介覺的合適,他會把反饋告知客戶,若是客戶也覺的合適,中介就會把租客和房東約在店裏進行更進一步的協商,若是能促成合同,那麼到此爲止中介完成了現階段的工做,剩下的事情,好比租客向房東每一個季度繳納房租,或者詢問房東家裏電器的使用方式,亦或者房東詢問房客租約到期是否續租等等,這些溝通均可以拋開中介去完成,也就是說,不必定須要中介做爲他們之間溝通的橋樑,但也並不意味着中介在之後的場景中是不可用的,好比,在租房過程當中有一些事情租客和房東有分歧,也能夠經過中介來進行協調和溝通。
還有一種中介的形式是,租客和房東是見不到彼此的,全程由中介負責,這種房叫託管房,租客不知道房東是誰,房東也不知道租客是誰,合同都和中介籤,咱們如今不討論這種狀況。
這張圖的目的一來是複述一下上面的場景描述,二來是讓你們直觀的看到它們三者交互的一個關係。
經過上訴場景的描述,和前一篇帖子中介紹的MVC各個對象的職責,想必他們到底誰是誰一目瞭然,咱們試着關聯一下這幾個對象
首先看中介,他負責了執行租客的招租要求,並告知房東這些要求,也就是說他做爲中介向房東轉告租客的需求,那中介就是咱們的Controller。
再次看租客,他是需求的發出者,他比其餘幾個對象更積極的提出需求,就比如View,老是再向Controller提出需求,也會更主動的向Model詢問狀態,就像租客同樣,遇到問題,要麼找中介,要麼去詢問房東,因此,在這裏把租客看做是View更加的貼切。
最後只剩下房東這個角色了,最後的Model就分配給房東吧。
一、View To Controller 求租房
上面這五張圖,是把MVC三個對象間的關係,以及案例中的業務流程作了一個「融合」,是一個抽象到具象的「融合」,架構圖中這些帶箭頭的線,是用來解釋對象間關係的,無非就是那些誰依賴誰的解釋,我同時又在線上加入了一些事件,是但願能把上面具象的問題引到抽象的架構中來,在實際中,這樣的融合是不符合常理的,好比你沒法用單個業務場景去定義對象間的依賴關係,好比View To Controller,難道只有求租房這個場景才能夠這麼用嗎?顯然不是的,包括各個角色中括號後面的稱呼。因此,爲了解決通用性的問題,人們把這樣的具象問題抽象成了一種依賴關係。
架構是藍圖,抽象的存在,而框架是具象的,它是爲了解決具體某一類問題而設計的。
在某些場合裏,須要咱們去介紹咱們的軟件架構,這就須要咱們具有描述架構的能力,我覺的能夠先描述一下具象的問題,再描述向上抽離的步驟,最後陳述抽象的產物,也就是咱們的架構,是一個不錯的思路。
經過上面大篇幅的拋磚引玉,相信你們對MVC的架構有了一個初步的認識,那麼接下來咱們開始真正的使用,還記得上一篇咱們使用了一個需求:
需求:列表加載數據而且展現
咱們依舊使用這個需求,由於前面定義好了三個層的類,接下來要作的是按照MVC的架構把他們拼裝起來:
開始加入到實際開發階段,咱們先建立一個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中,這些線和箭頭即是記錄着對象間的關係:
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 我是什麼?
//我是什麼動物?我是一隻貓
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
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(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能夠像圖三那樣去設計嗎?