模型視圖控制器(MVC)一個相當實用且十分流行的設計模式。作為一位稱職碼農,你不可能沒聽說過吧。 不幸的是它難以讓人理解。 在本文中,我將給出我認為是MVC的最簡單的解釋,以及為什么你應該使用它。
什么是模型 - 視圖 - 控制器(MVC)?
在一個典型應用中,你會發現這三個基本組成部分:
- 數據(模型)
- 查看和修改數據的接口(視圖)
- 可以對數據施加的操作(控制器)
MVC模式,簡言之,就是:
-
模型代表數據,除此之外別無它用。 模型不依賴於控制器或視圖。
-
視圖顯示模型數據,發送用戶動作(例如按鈕點擊)到控制器。 視圖可以:
-
獨立於模型和控制器的; 或者
-
作為控制器,因此依賴於模型。
-
-
控制器提供模型數據至視圖,解釋用戶的行為,如按鈕的點擊。 控制器依賴於視圖和模型。 在一些情況下,控制器和視圖可以合二為一。
規則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 , PersonListController和PersonListView。 還需要創建兩個類: PersonPhotoGridView和PersonPhotoGridController 。 在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/
