1. 程式人生 > >MMVVC設計方法介紹:經典MVC設計模式在Cocoa-MVC中的一種實現方法

MMVVC設計方法介紹:經典MVC設計模式在Cocoa-MVC中的一種實現方法

MVC介紹

MVC在WEB開發中用於界定工作屬於Server端還是WEB前端還是十分清晰的。這也就是大家目前常見的MVC設計模式:

View<===>Controller<===>Model

即Controller作為粘合劑分別於View和Model進行雙向通訊。

我也一度認為這就是經典MVC設計模式,雖然我一直知道MVC設計模式的起源是開發客戶端應用。但當我在做一個iOS移動端應用架構設計時發現,如果沿用Model到View的通訊必須藉助Controller,那麼一些資料變更引發View顯示變更的場景的實現是十分困難的。於是我增加了一種Model===>View單向通訊的方式,藉助一種特殊用途的物件:ModelView。稍後我會說明ModelView和MVVM中ViewModel的相似與不同。

在寫這篇文章前,我再次翻閱MVC設計模式的介紹時發現,原來WEB-MVC是經典MVC的變形,也就是說,在經典MVC中,Model的設計是可以向View進行單向通訊的,原文如下:

  • A Domain element was known as a Model and was ignorant of the user-interface (Views and Controllers)
  • Presentation was taken care of by the View and the Controller, but there wasn’t just a single View and Controller. A View-Controller pair was required for each element being displayed on the screen and so there was no true separation between them
  • The Controller’s role in this pair was handling user input (such as key-presses and click events) and doing something sensible with them
  • The Observer pattern was used to update the View whenever the Model changed

原文參見:Developing Backbone.js Applications:Smalltalk-80 MVC

  • Model(模型)物件與UI層(Views和Controllers)物件解耦
  • 顯示控制由VIew和Controller共同負責,但他們並不是獨立存在的,對每一個顯示元素來說,View物件和Controller物件是成對使用的,他們之間並沒有真正意義的解耦。
  • Controller在這個組合中的角色是處理使用者輸入(例如按鍵和點選事件),並對這些操作做出符合業務需求的處理。
  • 使用觀察者模式來將Model資料的變更通知View(層,即View+Controller)進行顯示更新。

因此我發現,我設計和使用的MMVVC設計方法其實就是經典MVC設計模式,只是對View、Controller、Model的概念又做了更細的分解,以便更好的實現模組複用,以及實現View、Controller、Model三層的開發解耦。

然後再看一段關於Smalltalk-80 MVC的設計意圖:

The idea was that decoupling these parts of the application would also allow the reuse of Models for other interfaces in the application.

也就是說,MVC中的Model是希望可以被任何需要展示這部分資料的UI所複用的。為什麼會有這個說明呢,是因為在那個年代,出現了這樣一種設計模式,原文說明如下:

An approach known as Separated Presentation began to be used as a means to make a clear division between domain objects which modeled concepts in the real world (e.g., a photo, a person) and the presentation objects which were rendered to the user’s screen.

文中提到了兩類Object:

Domain Object:也就是MVC中的Model

Presentation Object:用於表示顯示資料的物件

MVC的一個設計意圖就是通過複用Domain Object,而不是再增加一個叫做Presentation Object的概念。

如果你熟悉MVVM中的ViewModel,你是不是會覺得,ViewModel就是一種Presentation Object?而MVC則是希望通過複用Domain Object來消除Presentation Object。

MMVVC方法以及Objective-C實現

我之所以稱MMVVC為一種方法,是因為其本質就是Smalltalk-80 MVC設計模式/架構。只是在MVC設計模式的Model、View、Controller分層上又做了進一步的分層,以便進一步實現View模組的複用,以及實現Model、View、Controller三層的開發解耦(即一個客戶端功能的開發可以由三個開發人員同時分別開發View層,Model層、Controller層)。

在MVC中已經很清晰的描述了,Model層是與其他兩層解耦的,因此在MVC中Model層是可以獨立開發的。但對View和Controller的定義比較模糊,並強調View和Controller是成對出現的,他們直接並沒有真正的界限。這就讓開發者對什麼屬於View,什麼屬於Controller有了自由的理解,從而加劇了View和Controller中業務邏輯出現位置的混亂。

因此我在View和Controller之間,增加了一層ModelView,用來更加清晰明確的界定View和Controller,以及相關業務邏輯應屬於哪層:

  • Model:領域模型,用於處理和資料相關的操作或業務邏輯,如對資料的增刪改查,與UI層(View和Controller)解耦。
  • Domain-value Object:用於UI層顯示的資料項,隸屬Model層(即在進行資料查詢等操作時由Model層建立),對UI層僅暴露get方法(通過protocol實現),可通過觀察者模式(通過KVO實現)通知UI層資料發生了變更。
  • Controller:處理當前頁面中的使用者互動事件(互動邏輯是什麼),基本等同UIViewController。即Controller負責UI層互動相關的業務邏輯。
  • ModelView:用於特定資料的展示(應該如何顯示)。包括資料處理邏輯(如當name為空時顯示字串:未設定),和顯示樣式邏輯(如使用者頭像顯示時採用圓形剪裁或圓角剪裁),並對DO進行監聽,在DO屬性發生變化時更新View。與Controller層解耦,通過觀察者模式與Domain-value Object耦合,可以與Model層耦合,用於顯示資料的獲取,但不應用於資料修改的提交(即處理使用者互動事件的結果)。
  • View:和業務邏輯完全無關的可複用的View元件,如UIImageView,與Model和Controller解耦。

通過上述分層和ModelView的引入,MMVVC方法對UI層相關業務邏輯的所屬位置進行了更為明確的定義,將UI層相關業務邏輯分別劃分到了Controller和ModelView,這樣,不同的開發人員就可以分別編寫一個功能所需的Model層業務邏輯、UI層業務邏輯、特殊需求的可複用View元件。

同時,通過對Model層以及View模組進行合理的設計,甚至在開發一個新功能時,僅需要編寫UI層業務邏輯程式碼(Controller和ModelView)。

ModelView與ViewController

在MVC設計模式的觀點中提到,View和Controller一定是成對出現的,Controller負責處理UI業務邏輯中的使用者事件,那顯然,UI業務邏輯中其餘的業務邏輯(如ViewModel中定義的資料轉換邏輯)應隸屬於View。個人猜測在Smalltalk-80系統下,顯示邏輯的複雜程度應該是遠遠小於當今的,因此即便View的顯示邏輯歸屬於View也不會有問題,然而在當前View日益複雜的今天,特別是View元件的廣泛使用,於是大量開發者開始將View顯示邏輯放在Controller中。這就是ModelView的作用:處理使用者顯示邏輯,將不屬於Controller中的顯示邏輯重新放回View層。

ViewController中的view屬性則可視為為一個ModelView。但在顯示元件使用方便的今天,為每個ViewController編寫一個ModelView類可能並不是一個好的選擇,因此,我們可以在loadView中,通過程式碼,或Storyboard來組裝出一個ModelView。僅在一個View需要對資料進行復雜處理邏輯時,或有複雜顯示邏輯需要處理時,編寫特定的ModelView類,並通過Controller將DO物件設定給這個ModelView。

同時,ModelView可以與Model層耦合,以獲取資料,即原Controller中負責資料獲取的業務邏輯也應放在ModelView中,但ModelView不應處理使用者輸入(如對頁面資料修改後通知Model修改),這部分邏輯是Controller的職責。

ModeView與ViewMode

與ViewModel的相同點是,都負責處理對Mode層資料向顯示層資料的轉換邏輯。

不同點:

ViewModel:隸屬於Controller層(由Controller物件建立),屬於Presentation Object。

ModeView:隸屬於View層,實現為View類(如UIView)的子類,可直接用於顯示。ModeView可以呼叫Model中資料獲取的相關方法,獲取用於顯示的資料。

結論

MMVVC方法是在Smalltalk-80 MVC設計模式的基礎上,通過增加ModelView層,對UI層中業務邏輯的編寫進行了更為明確的定義:

  • Controller:使用者輸入事件處理相關業務邏輯
  • ModeView:UI顯示相關業務邏輯
  • View:業務邏輯無關的顯示元件

通過將UI層業務邏輯的分離,為實現View、Controller、Model三層的開發解耦提供了一種可能性。