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

目錄

MVP那些事兒(1)……用場景說話設計模式

MVP那些事兒(2)……MVC架構初探緩存

MVP那些事兒(3)……在Android中使用MVC(上)服務器

MVP那些事兒(4)……在Android中使用MVC(下)架構

MVP那些事兒(5)……中介者模式與MVP的關係框架

MVP那些事兒(6)……MVC變身爲MVPide

MVP那些事兒(7)……Repository設計分析函數

爲何要先介紹MVC?

若是你要想更佳深入的理解MVP,並在實際開發中靈活的應用,那麼就要先了解它的低配版MVC,他倆只是一步之遙,先了解MVC再學習MVP,MVP的優點才能凸顯出來,這樣連貫性的學習纔會加深對MVP的理解。佈局

回顧MVP那些事兒(2) 初探MVC架構的內容:post

MVC的做用?它是設計模式嗎?是框架嗎?是架構嗎? MVC各個層的定義和職責學習

經過上一篇的內容,你們也對MVC已經有了一個大體的瞭解,在開啓這一章內容前,但願你們能先閱讀上一篇的內容,不然可能會「斷片」。 在上一篇中,咱們詳細的介紹了MVC三個層的基本職責,咱們再回顧一下這三個「片斷」:

Model層

一、JavaBean不是Model,但Model也能夠包含JavaBean的職責,但不是必須的。 二、Model是用來處理數據的,如獲取數據(本地或者服務端),數據處理,如CURD。

View層

一、它的主要職責爲呈現Model的數據、主動詢問狀態或被動的監聽 二、通知控制器Controller去處理一些事情 三、接收Controller,編輯本身與Model無關的狀態

Controller

一、接收View的操做,並轉調給Model 二、改變View的狀態

同時還遺留了兩個問題:

一、如何把這三個片斷組裝起來?在Android裏怎麼寫? 二、view在執行tasksRepository.getTaskCache()時,是怎麼知道tasksRepository數據已經準備好了呢?怎麼通知view的?

把片斷「組合」起來

在模式設計時,咱們會經過關係圖來反映對象間的關係,以下圖:

MVC架構圖

這是一張很是經典的MVC架構圖(手殘,有點醜……),相信你們對這張圖已經不陌生了,OK,這張圖我先放下不表,由於考慮到咱們介紹的是架構,自己是抽象的,加上如此抽象的圖,怕是不能好好的切入,因此我想盡量的巨像化這個過程,當你們有一個清晰的理解後,再回過頭來從新審視它。我原本是想放在文章的後半段祭出這張圖的,但想了想有點不妥,緣由是咱們上期已經經過一整篇用來初探MVC,也該把MVC的架構圖貼出來應一應景了……

那麼MVC這三者的關係到底是怎樣的呢?

咱們依舊經過現實中的場景來描述一下MVC三者的關係,讓你們對這三者的關係有一個初期的直觀的理解,起到一個拋磚引玉的效果,廢話很少,場景開始。

場景

想必你們都租過房子,有當租客的體驗,也有一部分人可能還有當過過房東經驗,在當下的社會中,房屋中介是咱們租房找房必不可少的環節,我相信大部分人都和中介打過交道,咳咳,那咱們就還原一下租房的過程,

首先,肯定角色

在這個場景裏包含了三個角色,租客、中介、和房東,

其次,選擇角度

咱們從租戶的角度描述一下租房的場景,只考慮正向流程。

再次,場景描述

租客要先找到中介並描述本身須要的房子,中介接到需求後,經過篩選發現有一個房東很適合,因而中介開始與這個房東接觸,詢問一些和房子租金相關的事宜,若是中介覺的合適,他會把反饋告知客戶,若是客戶也覺的合適,中介就會把租客和房東約在店裏進行更進一步的協商,若是能促成合同,那麼到此爲止中介完成了現階段的工做,剩下的事情,好比租客向房東每一個季度繳納房租,或者詢問房東家裏電器的使用方式,亦或者房東詢問房客租約到期是否續租等等,這些溝通均可以拋開中介去完成,也就是說,不必定須要中介做爲他們之間溝通的橋樑,但也並不意味着中介在之後的場景中是不可用的,好比,在租房過程當中有一些事情租客和房東有分歧,也能夠經過中介來進行協調和溝通。

還有一種中介的形式是,租客和房東是見不到彼此的,全程由中介負責,這種房叫託管房,租客不知道房東是誰,房東也不知道租客是誰,合同都和中介籤,咱們如今不討論這種狀況。

來個圖描述一下上訴的情景

好隨便的圖

這張圖的目的一來是複述一下上面的場景描述,二來是讓你們直觀的看到它們三者交互的一個關係。

中介、租客、房東,把他們和MVC的三個角色關聯起來

經過上訴場景的描述,和前一篇帖子中介紹的MVC各個對象的職責,想必他們到底誰是誰一目瞭然,咱們試着關聯一下這幾個對象

首先看中介,他負責了執行租客的招租要求,並告知房東這些要求,也就是說他做爲中介向房東轉告租客的需求,那中介就是咱們的Controller。

再次看租客,他是需求的發出者,他比其餘幾個對象更積極的提出需求,就比如View,老是再向Controller提出需求,也會更主動的向Model詢問狀態,就像租客同樣,遇到問題,要麼找中介,要麼去詢問房東,因此,在這裏把租客看做是View更加的貼切。

最後只剩下房東這個角色了,最後的Model就分配給房東吧。

接下來咱們用MVC的方式來梳理一下中介、租客、房東的關係

一、View To Controller 求租房

二、Controller To Model 詢問:出租嗎?
三、Controller To View 告知:找到房源
四、View To Model 單聊:能便宜嗎?
五、Model To View 單聊:沒門兒!

上面這五張圖,是把MVC三個對象間的關係,以及案例中的業務流程作了一個「融合」,是一個抽象到具象的「融合」,架構圖中這些帶箭頭的線,是用來解釋對象間關係的,無非就是那些誰依賴誰的解釋,我同時又在線上加入了一些事件,是但願能把上面具象的問題引到抽象的架構中來,在實際中,這樣的融合是不符合常理的,好比你沒法用單個業務場景去定義對象間的依賴關係,好比View To Controller,難道只有求租房這個場景才能夠這麼用嗎?顯然不是的,包括各個角色中括號後面的稱呼。因此,爲了解決通用性的問題,人們把這樣的具象問題抽象成了一種依賴關係。

架構是藍圖,抽象的存在,而框架是具象的,它是爲了解決具體某一類問題而設計的。

MVC架構圖是怎麼來的?拋去具象,看本質

具象到抽象三連圖
抽象到最後就是咱們開篇的第一張圖。

在某些場合裏,須要咱們去介紹咱們的軟件架構,這就須要咱們具有描述架構的能力,我覺的能夠先描述一下具象的問題,再描述向上抽離的步驟,最後陳述抽象的產物,也就是咱們的架構,是一個不錯的思路。

應用到實際中

經過上面大篇幅的拋磚引玉,相信你們對MVC的架構有了一個初步的認識,那麼接下來咱們開始真正的使用,還記得上一篇咱們使用了一個需求:

需求:列表加載數據而且展現

咱們依舊使用這個需求,由於前面定義好了三個層的類,接下來要作的是按照MVC的架構把他們拼裝起來:

在Android中使用

開始加入到實際開發階段,咱們先建立一個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中,這些線和箭頭即是記錄着對象間的關係:

按照架構圖的提示,咱們爲TasksRepository、TasksView、TasksController這三個類梳理了一個大體的依賴關係,同時MainActivity被撂到了一邊,並非它和這三個對象沒有關係,而是它如今還不能直接用,它須要變一下,爲何這麼說呢,來看例子:

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 與 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

同時更新一下代碼,咱們讓MainActivity實現一個TasksView,賦予它View的職責

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的初始化

那麼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能夠像圖三那樣去設計嗎?

相關文章
相關標籤/搜索