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


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

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

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

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

MVC模式,簡言之,就是:

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

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

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

    • 作為控制器,因此依賴於模型。

  3. 控制器提供模型數據至視圖,解釋用戶的行為,如按鈕的點擊。 控制器依賴於視圖和模型。 在一些情況下,控制器和視圖可以合二為一。

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

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

以一個地址簿應用程序為例。 模型是一些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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM