模型 - 視圖 - 控制器(MVC)詳解

模型視圖控制器(MVC)一個至關實用且十分流行的設計模式。做爲一位稱職碼農,你不可能沒據說過吧。 不幸的是它難以讓人理解。 在本文中,我將給出我認爲是MVC的最簡單的解釋,以及爲何你應該使用它。redis

什麼是模型 - 視圖 - 控制器(MVC)?

在一個典型應用中,你會發現這三個基本組成部分:設計模式

  • 數據(模型)
  • 查看和修改數據的接口(視圖)
  • 能夠對數據施加的操做(控制器)

MVC模式,簡言之,就是:工具

  1. 模型表明數據,除此以外別無它用。 模型不依賴於控制器或視圖。佈局

  2. 視圖顯示模型數據,發送用戶動做(例如按鈕點擊)到控制器。 視圖能夠this

    • 獨立於模型和控制器的; 或者spa

    • 做爲控制器,所以依賴於模型。翻譯

  3. 控制器提供模型數據至視圖,解釋用戶的行爲,如按鈕的點擊。 控制器依賴於視圖和模型。 在一些狀況下,控制器和視圖能夠合二爲一。設計

規則1是MVC的黃金法則,我再重複一遍:code

模型表明數據,除此以外別無它用。 模型不依賴於控制器或視圖。對象

以一個地址簿應用程序爲例。 模型是一些Person對象,視圖是一個GUI窗口,顯示聯繫人列表,而且控制器處理用戶行爲諸如「刪除聯繫人」,「添加聯繫人」,「發郵件至聯繫人」等。下面的例子沒有使用MVC,由於模型依賴於視圖。

//Example 1:
void Person::setPicture(Picture pict){
    m_picture = pict; //set the member variable
    m_listView->reloadData(); //update the view
}

下面的示例使用了MVC:

//Example 2:
void Person::setPicture(Picture pict){
    m_picture = pict; //set the member variable
}

void PersonListController::changePictureAtIndex(Picture newPict, int personIndex){
    m_personList[personIndex].setPicture(newPict); //modify the model
    m_listView->reloadData(); //update the view
}

在上面的例子中, Person類並不知道視圖的存在。 PersonListController負責模型修改和視圖更新。視圖窗口告訴控制器用戶的行爲(就上述狀況而言,它將提醒控制器用戶修改了一個聯繫人的照片)。

MVC的優點在哪裏?

沒必要要的複雜性是軟件開發的夢魘。 它致使軟件漏洞百出,維護費用昂貴。而處處引入依賴很容易就會形成代碼過於複雜。 相反地,若是咱們移除沒必要要的依賴能夠改善代碼質量,維護上更容易,由於代碼可重複使用而無需修改。 你能夠安心地複用舊的、穩定的代碼不用顧慮招致新的bug。

MVC設計模式的主要優勢是:

MVC使得模型類不加修改便可重複使用。

控制器存在的目的是消除模型與視圖依賴關係。 從模型中移除視圖依賴後,模型代碼變得整潔起來。

爲何模型代碼能這麼小清新? 讓咱們繼續以地址簿應用爲例。 項目經理找到碼農們,對他們說 」我很欣賞聯繫人列表窗口,但咱們須要另外一個窗口顯示全部聯繫人的照片,這些照片應該是處於一個表格佈局中,每排五張照片。」

若是應用程序採用了MVC,這個任務至關簡單。 目前主要有三個類: Person  PersonListControllerPersonListView。 還須要建立兩個類: PersonPhotoGridViewPersonPhotoGridController 。 Person類保持不變的狀況下,很容易插入兩種不一樣的視圖。 怎麼樣啊!

若是應用程序具備例1中相似的結構,任務立馬變得棘手了。此時有兩個類Person 和PersonListView 。Person類不能插入另外一種視圖,由於它包含了涉及PersonListView類的具體代碼 。 開發人員必須修改Person類以適應新的PersonPhotoGridView ,並最終像這樣複雜化模型:

//Example 3:
void Person::setPicture(Picture pict){
    m_picture = pict; //set the member variable
    if(m_listView){ //check if it's in a list view
        m_listView->reloadData(); //update the list view
    }
    if(m_gridView){ //check if it's in a grid view
        m_gridView->reloadData(); //update the grid view
    }
}

如你所觀察到的同樣,模型代碼開始變得苦澀。 若是項目經理接着發話:「咱們正在移植的應用程序到一個使用不一樣的GUI庫的平臺」,簡潔性此時尤其突出。 採用MVC的Person類能夠經過不一樣的GUI工具包顯示無需任何修改。 單單建立一個控制器,並用新的庫中的視圖,就像你用舊的庫同樣。 若是沒有MVC,支持多種GUI工具包將是一場噩夢。 最終代碼可能會這樣的:

//Example 4:
void Person::setPicture(Picture pict){
    m_picture = pict;
#ifdef ORIGINAL_GUI_TOOLKIT
    if(m_listView){ //check if it's in a list view
        m_listView->reloadData(); //update the list view
    }
    if(m_gridView){ //check if it's in a grid view
        m_gridView->reloadData(); //update the grid view
    }
#endif
#ifdef NEW_GUI_TOOLKIT
    if(m_listView){ //check if it's in a list view
        m_listView->redisplayData(); //update the list view
    }
    if(m_gridView){ //check if it's in a grid view
        m_gridView->redisplayData(); //update the grid view
    }
#endif
}

setPicture方法基本上會亂上一團。

爲何不把控制器代碼置入視圖?

一直解決例4中的尷尬代碼的方案是將控制器代碼從模型移動到視圖,像這樣:

//Example 5:
PersonListView::newPictureClicked(Picture clickedPicture){
    m_selectedPerson.setPicture(clickedPicture);
    this->reloadData();
}

上述例子也可以使模型易於複用,這是MVC的主要優點。 當視圖將只顯示一種類型的模型對象,合併視圖和控制器是比較合適的。 例如,一個SinglePersonView將只顯示一個Person的對象,因此SinglePersonView可兼做控制器。

然而,若是控制器與視圖分離,MVC有第二個優勢:

MVC可使視圖可重複使用的而無需修改。

MVC模型不只使得模型簡潔,視圖一樣如此。 理想狀況下,列表視圖應該可以顯示的任何列表,不僅是Person的對象。 例5中的代碼不能是一個通用的列表視圖,由於它與模型耦合在一塊兒(Person類)。要想視圖(如列表視圖,或表格視圖)和模型代碼同時可重複使用,MVC是惟一的選擇。 控制器移除模型和視圖間的依賴關係,這使得它們在其餘地方能夠被複用。

結論

MVC設計模式在視圖和模型間插入控制器類,移除視圖和模型間的依賴。 模型以及視圖,可重複使用而無需修改。 這樣,實現新的功能和維護變得垂手可得。 該用戶很快獲得穩定的軟件,該公司節省了資金,並且開發人員不發瘋。 這樣好不?

翻譯來源:http://www.tomdalling.com/blog/software-design/model-view-controller-explained/

相關文章
相關標籤/搜索