朱立(北京信息職業技術學院,北京 100018)
?
淺談MVVM數據綁定的耦合性
朱立
(北京信息職業技術學院,北京 100018)
【摘 要】復雜軟件需要清晰合理的架構設計,否則會給前期開發和后期維護帶來極大的困難。各類框架的應運而生,重要目的之一就是降低軟件的耦合度以獲得良好的性能。本文圍繞近年來流行度較高的MVVM架構,簡單介紹了其原理和特點,針對MVVM數據綁定的耦合性,分別例舉逐一屬性綁定、對象綁定和集合綁定,并針對不同的綁定配置方式分析了耦合性。
【關鍵詞】MVVM 數據綁定 耦合
對軟件來說,最基本、最核心的內容是數據,所有代碼層次的劃分都是圍繞數據設計的。業務邏輯層是為了實現對數據的增刪改查,各類界面技術是為了更好地呈現數據。隨著軟件復雜度的提高,要想降低數據層、業務邏輯層及界面層的耦合度,就需要良好的架構設計。ORM等框架實現了業務邏輯層和數據層之間的解耦合,另有一批框架用來解決業務邏輯層和視圖之間的耦合問題,MVVM就是其中之一。
Model-View-ViewModel(MVVM,模型-視圖-視圖模型)是一種架構模式,如圖1所示。MVVM的目標是將幾乎所有程序代碼從視圖中移除(更加徹底的代碼后置)。交互設計師只需專注于使用XAML(視圖)表達用戶體驗需求,并創建視圖和視圖模型的綁定;而模型和視圖模型則由程序員進行開發和維護。
MVVM的視圖具有獨立性,其中的控件能夠管理部分用戶輸入并且自行反應。用戶輸入經過視圖底層系統的處理和分發,多以事件的形式被后臺用戶程序所感知。視圖模型包含概念模型而不是數據模型,所有業務邏輯和其它操作都是在模型和視圖模型里完成的。
長久以來,在非MVVM架構中直接編寫代碼存在兩個問題。一,View中有些控件的數據類型和Model中的屬性類型不同。例如性別,Model中通常設置為bool類型;而View中呈現的是“男”、“女”這樣的字符串,這就需要額外的代碼進行轉換。而轉換代碼如果放在View中會增大軟件的耦合度(View上不應出現邏輯代碼);放在Model中,會導致Model臃腫龐大。二、事件驅動開發中的復用問題。Winform開發中的事件驅動因其易于理解和實現而被廣泛接受,問題就是多種同類事件并存,后期維護繁重,且代碼復用性相對較差。

圖1 MVVM結構關系圖
Model和View中間的輔助角色——ViewModel較好地解決了上述兩個問題。它幫助View和Model之間進行數據轉換,并保證數據類型適當。同時它也將View的多種命令綁定到Model中相同的處理方法上,使得這些命令中綁定的方法可以被其他View復用。與同類架構模式(MVP、MVC)相比,MVVM架構唯一不同的特點是它采用雙向綁定,即View中的變動會自動反映在ViewModel中,反之亦然。
MVVM的一個主要特征是,通過Binding機制由ViewModel指示View如何作變更;用戶與ViewModel的交互通過Command來實現,這時只需View指導ViewModel即可,而ViewModel不必關心View如何展示和展示什么,它只需提供屬性(要綁定的數據和Command)并實現INotifyPropertyChanged。少了一個方向的依賴,代碼自然更簡潔清晰。
View中的控件存在一個屬性“DataContext”,負責為控件指定數據源頭。DataContext屬性給控件指定一個后臺模型,就是該控件的數據來源。ViewModel就是這個后臺模型,給View中的控件提供用于顯示的數據。而ViewModel提供的數據應該來自于背后的Model。所以,三者的關系是根據View中顯示的數據是何種Model,來定義ViewModel。
Model中的某個類要成為真正的數據源,需要實現以下兩個步驟。
(1)在后臺.cs文件(以下簡稱后臺)中為該類定義屬性。如:

(2)讓該類實現INotifyPropertyChanged接口。實現該接口的目的是當數據源的屬性值改變后通知Binding(使其知道源數據變了并進行聯動協同),以便Binding把數據傳輸給目標。實際上,這個動作本質上還是運用了事件機制,只是掩蓋在底層,不用程序員去寫代碼。


具體的綁定需要在前臺頁面文件(以下簡稱前臺)和后臺(如MainPage類的構造函數中)分別進行配置:
后臺配置:

前臺配置:

〈TextBox x:Name="tb1" Text="{Binding Names}"〉〈/ TextBox〉語句的含義是,名稱為tb1的文本框的Text屬性值與Names屬性值相關聯,Names變化的時候,Text也隨之變化。
這里面蘊含了“數據驅動界面”的模型。在這里,Names屬性值是數據源,文本框tb1用來顯示數據,Names屬性值驅動文本框tb1的Text進行改變;人為改變文本框tb1的Text屬性值,也會被送回到person對象的Names屬性上去。這里的Text屬性也叫“依賴式屬性”,就是它本身沒有值,它的值“依賴”在其它對象的屬性值上,通過Binding進行傳遞和轉換。這樣做的好處是,隨便給Binding指定一個數據源,只要這個數據源有名為“Names”的屬性,Binding就會自動提取它的值并傳輸給Text。
不同配置的數據綁定會呈現不同的耦合性。在上述例子中,需要將前臺頁面中的控件逐一綁定到person對象的每個屬性,屬性越多,綁定越多,前后臺的耦合度也就越高。在數據結構復雜度很高的情況下,這種方式顯然不可取。可以采取下列方式進行改進。
后臺文件:

前臺文件:

這個綁定是將前臺頁面(Page就是App.MainPage,所以可以在類代碼中使用this)綁定到person對象(包含所有的屬性)上,該頁面的所有控件就可以直接獲取該對象的所有屬性值而不必單獨進行綁定,耦合度大大降低。
〈TextBox Text="{Binding Names}"〉〈/TextBox〉語句中,Text屬性綁定的Names屬性是從所在頁面(已經綁定了person對象)獲取的,而不是后臺類對象,所以這里不構成耦合。
如果后臺數據結構不是單一對象而是集合,并且是存在上下級關系的復雜集合,用這種改進的方法也存在前后臺耦合度過高的問題(集合中的每個對象都要逐一與前臺頁面進行綁定)。這時可以采取集合綁定的方法。
后臺文件:

前臺文件:

在上例的前臺文件中,只有Source="{Binding Company.Departments}" x:Key= "cvsDeps"〉這一個綁定與后臺構成了耦合,它綁定了后臺集合對象Company.Departments。后續的綁定都是調用這個復雜集合的下級對象。雖然調用關系復雜,但耦合度并沒有提高。
通過分析可以看出,綁定的設置方式不同,前后臺的耦合度也會不同。而綁定設置方式應該結合數據結構的復雜程度和數據展示的需求進行考慮。軟件的靈魂是數據,最終要在滿足需求的情況下保證軟件性能。
參考文獻:
[1]劉黎志,陳傳波.Windows Phone數據訪問技術研究.計算機工程與科學[J],2014,36(9).
[2]陳濤.MVVM設計模式及其應用研究.計算機與數字工程[J].2014, (10).
作者簡介:朱立,女,北京信息職業技術學院,副教授,碩士,軟件技術、高職教育研究方向。