Implementing the MVVM pattern (Part 1)

Mẫu thiết kế MVVM đưa ra cách tách biệt rõ rệt giữa UI, presentation logic, business logic và dữ liệu của ứng dụng bằng cách tách thành các lớp riêng biệt. Các tương tác giữa View và ViewModel là phần quan trọng nhất để tìm hiểu, nhưng các tương tác giữa các model class và ViewModel cũng quan trọng không kém. Nội dung trình bày sau đây mô tả các pattern khác cho các tương tác này và mô tả cách thiết kế chúng khi bạn thực thi mẫu thiết kế MVVM trong các ứng dụng của bạn.

Data Binding

Kết buộc dữ liệu đóng một vai trò quan trọng trong MVVM. Cả WPF và Silverlight đề cung cấp những khả năng kết buộc dữ liệu mạnh mẽ. ViewModel và các model class của bạn có thể được thiết kế để hỗ trợ việc kết buộc dữ liệu để chúng có thể tận dụng những khả năng này của chúng. Thông thường, điều này có nghĩa là chúng phải thực thi đúng các interface.

Kết buộc dữ liệu của Silverlight và WPF hỗ trợ  nhiều chế độ kết buộc dữ liệu. Với việc kiết buộc “1 chiều” (one-way), các UI control có thể giới hạn cho một ViewModel để chúng có thể “phản chiếu” giá trị của dữ liệu bên dưới khi việc hiển thị được render. Kết buộc “2 chiều” (two-way) cũng sẽ tự động cập nhật dữ liệu bên dưới khi người dùng định nghĩa nó trong UI.

Để đảm bảo UI luôn được cập nhật khi dữ liệu thanh đối trong ViewModel, nó phải thực thi interface thông báo thay đổi tương ứng. Nếu nó định nghĩa các thuộc tính có thể là giới hạn dữ liệu, nó sẽ thực thi INotifyPropertyChanged interface. Nếu ViewModel mô tả một collection, nó sẽ thực thi INotifyCollectionChanged interface hoặc chuyến hóa từ lớp ObservableCollection<T> cung cấp cách thực thi của interface này. Cả hai interface này đều định nghĩa một sự kiện được đưa ra bất kể khi nào dữ liệu bên dưới thay đổi. Bất kì control giới hạn dữ liệu nào cũng sẽ được tự động cập nhật khi các sự kiện này được tạo ra.

Trong nhiều trường hợp, một ViewModel sẽ định nghĩa các thuộc tính trả về các đối tượng. Khả năng kết buộc dữ liệu trong WPF và Silverlight hỗ trợ kết buộc các thuộc tính lồng nhau thông qua thuộc tính Path. Hơn nữa, việc ViewModel của View trả về các tham chiếu đến ViewModel hoặc các model class khác là rất thường xuyên. Tất cả ViewModel và các model class có khả năng truy xuất đến View sẽ thực thi các interface INotifyPropertyChanged hoặc INotifyCollectionChanged tương ứng.

Implementing INotifyPropertyChanged

Việc thực thi INotifyPropertyChanged interface trong ViewModel hoặc các model class cho phép chúng cung cấp các thông báo thay đổi (change notification) đến bất kì control giới hạn dữ liệu nào (data-bound control) trong View khi giá trị của thuộc tính bên dưới thay đổi. Ví dụ (xem Questionnaire class trong demo)

questionnaire-class

Việc thực thi INotifyPropertyChanged interface trên nhiều ViewModel class có thể lăp lại và error-prone bởi việc cần thiết xác định tên thuộc tính trong tham số của sự kiện. Prism Library cung cấp một lớp cơ sở mà bạn có thể chuyển hóa các ViewModel class thực thi iNotifyPeropersyChanged interface

INotifyPropertyChanged

Một ViewModel class được kế thừa có thể tạo ra sự kiện thay đổi thuộc tính bằng việc thực thi RaisePropertyChanged với tên thuộc tính được xác định hoặc sử dụng một lamda expression để chỉ ra thuộc tính

RaisePropertyChanged

Thông thường, Model và ViewModel của bạn sẽ bao gồm các thuộc tính mà các giá trị của chúng được tính toán từ những thuộc tính khác trong Model hoặc ViewMode. Khi xử lý những thay đổi đối với các thuộc tính này phải đảm bảo rằng các sự kiện thông báo cũng phải được tạo ra cho bất kì thuộc tính được tính toán nào.

Implementing INotifyCollectionChanged

ViewModel hoặc Model class của bạn có thể mô tả một collection của các item, hoặc nó có thể định nghĩa một hoặc nhiều thuộc tính trả về một collection của các item. Trong các trường hợp này, bạn sẽ muốn hiển thị collection trong một ItemControl như ListBox hoặc DataGrid control trong View. Những control này có thể giới hạn dữ liệu cho một ViewModel mà mô tả một collection hoặc một thuộc tính trả về một collection thông qua thuộc tính ItemSource.

ItemSource-DataGrid

Để hỗ trợ các yều cầu thông báo thay đổi tức khắc, ViewModel hoặc Model class, nếu nó mô tả một collection, nên thực thi INotifyCollectionChanged interface (trong trường hợp khác có thể là INotifyPropertyChanged interface). Nếu ViewModel hoặc Model class xác định một thuộc tính mà trả về một tham chiếu đến một collection, collection class được trả về nên thực thi INotifyCollectionChanged interface.

Tuy nhiên, việc thực thi INotifyCollectionChanged interface có thẩ là thách thức vì nó cung cấp các notification khi các item được thêm vào, xóa (hủy) hoặc bị thay đổi trong collection. Thay vì thực thi trực tiếp interface này, nó thường dễ dàng sử dụng hoặc chuyển hóa từ một collection class thực thi nó rồi. ObservableCollection<T> class cung cấp một sự thực thi cho interface này và được sử dụng phổ biến như một lớp cơ sở hoặc thực thi các thuộc tính mô tả một collection của các item.

ObservableCollection

Nếu bạn có một tham chiếu đến một collection class (chẳng hạn như từ thành phần khác hoặc service không thực thi INotifyCollectionChanged), thông thường bạn có thể “gói’” trong một đối tượng ObservableCollection<T> sử dụng một trong các hàm dựng lấy một tham số IEnumerable<T> hoặc List<T>.

Implementing ICollectionView

Đoạn code trước cho thấy cách thực thi một thuộc tính ViewModel đơn giản trả về một collection các item có thể được hiển thị thông qua các control giới hạn dữ liệu trong View. Vì ObservableCollection<T> class thực thi INotifyCollectionChanged interface, các control trong View sẽ được tự động cập nhật để “chiếu’” danh sách các item trong collection khi các item được thêm vào hay bị xóa đi.

Tuy nhiên, thông thường bạn sẽ cần nhiều sự kiểm soát hơn về cách collection các item được hiển thị trong View, hoặc theo dõi sự tương tác của người dùng với collection các item được hiển thị trong bản thân ViewModel. Ví dụ, bạn có thể cần phải cho phép collection các item được chọn lọc hoặc sắp xếp theo presentation logic được thực thi trong View, hoặc bạn có thể cần theo dõi item được chọn hiện thời trong View để các câu lệnh được thực thi trong ViewModel có thể thực thi trên item được chọn đó.

WPF và Silverlight hỗ trợ những trường hợp này bằng việc cung cấp các class khác nhau thực thi ICollectionView interface. interface này cung cấp các thuộc tính và phương thức cho phép một collection được chọn lọc, sắp xếp hoặc gom nhóm lại, và cho phép item được chọn hiện tại được thay đổi hoặc được theo dõi. Cả WPF và Silverlight cung cấp các implementation cho interface này – Silverlight cung cấp PagedCollectionView class, WPF cung cấp ListCollectionView class.

Các collection view class có thể được sử dụng bởi ViewModel để theo dõi thông tin trạng thái quan trọng cho collection bên dưới, trong khi việc bảo trì một sự tách biệt rõ ràng của các mối quan tâm giữa UI trong View và dữ liệu bên dưới trong Model. Để hiệu quả, các CollectionView là các ViewModel được thiết kế đặc biệt để hỗ trợ các collection.

Hơn nữa, nếu bạn cần thực thi việc chọn lọc, sắp xếp, gom nhóm hoặc theo dõi các item được chọn trong ViewModel của bạn, ViewModel của bạn nên tạo ra một đối tượng của một view collection class cho mỗi collection được đưa đến View. Sau đó bạn có thể đăng kí các selection changed event chẳng hạn như CurrentChanged event, hoặc điều khiển việc chọn lọc, sắp xếp hoặc gom nhóm bằng cách sử dụng các phương thức được cung cấp bởi collection view class trong ViewModel của bạn.

ViewModel nên thực thi một thuộc tính chỉ đọc trả về một ICollectionView reference để các control trong View có thể kết buộc dữ lêệu vào collection view object và tương tác với nó. Tất cả control của WPF và Silverlight mà chuyển hóa từ ItemsControl base class có thể tự động tương tác với các ICollectionView class.

Đoạn code sau minh họa cho việc sử dụng PagedCollectionChanged trong Silverlight để theo dõi customer được chọn hiện tại.

PageCollectionChanged

Trong View, bạn có thể kết buộc một ItemsControl, chẳng hạn như ListBox, vào thuộc tính Customers trên ViewModel thông qua thuộc tính ItemsSource của nó.

Customers-ItemsSource

Khi người dùng chọn một customer trong UI, ViewModel sẽ được thông báo để nó có thể thực thi các câu lệnh liên quan đến customer được chọn hiện tại. ViewModel cũng có thể thay đổi theo như lập trình chọn lựa hiện tại trong UI bằng việc gọi các phương thức trên collection view object.

customer-next

Khi selection thay đổi trong collection view, UI tự động cập nhật để mô tả trạng thái được chọn của item. Việc thực thi cũng tương tự đối với WPF mặc dù PagedCollectionChanged trong ví dụ trước thông thường sẽ bị thay thế bằng một ListCollectionView hoặc BindingListCollectionView class.

customers-binding

MVVM Pattern (Lý thuyết)

Mẫu thiết kế Model-View-ViewModel (MVVM) giúp tách biệt lớp business và presentation của ứng dụng khỏi phần giao diện (UI) của nó. Việc duy trì phần tách biệt giữa lớp ứng dụng và UI giúp chỉ ra những vấn đề trong việc phát triển và thiết kế và có thể giúp ứng dụng của bạn trở nên dễ dàng để kiểm thử, bảo trì và phát triển hơn.

Bằng việc sử dụng mẫu thiết kế MVVM, phần giao diện (UI) của ứng dụng, presentation và business được tách thành 3 lớp riêng biệt: View bao hàm UI và UI logic (code behind); View Model bao hàm presentation và state; Model bao hàm business và data.

Các lớp trong MVVM và sự tương tác giữa chúng

IC448690

Tương tự như những mẫu thiết kế có sự tách biệt phần presentation khác, điểm mấu chốt trong việc sử dụng mẫu MVVM hiệu quả nằm ở việc hiểu cách thích hợp để tác động mã nguồn của chương trình vào đúng những lớp cần thiết, và hiểu những cách thức mà những lớp này tương tác trong những ngữ cảnh khác nhau.

View Class

Chịu trách nhiệm xác định cấu trúc và thể hiện những gì mà người dùng thấy trên màn hình. Trong trường hợp lý tưởng, phần code-behind của lớp View chỉ cần duy nhất một constructor gọi phương thức InitializeComponent. Trong một số trường hợp, code-behind có thể bao gồm đoạn mã UI logic thực thi visual behavior khó hoặc không hiệu quả để thể hiện trong XAML, chẳng hạn như các chuyển động phức tạp, hoặc khi mã nguồn cần tính toán một cách chính xác các thành phần hiển thị của view.

Trong Silverlight và WPF, các expression cho việc kết buộc dữ liệu (data bingding) trong view được thực hiện thông qua data context của nó. Trong MVVM, data context của nó được đặt cho view model. View model thực thi các thuộc tính (property) và các dòng lệnh (command) mà view có thể kết buộc và thông báo cho view biết mỗi khi có thay đổi thông qua các sự kiện thông báo thay đổi. Đó hoàn toàn là mối quan hệ một-một giữa view và view model của nó.

Nhìn chung, view là những lớp dẫn xuất Control (Control-derived classes) hoặc lớp dẫn xuất UserControl (UserControl-derived classes). Tuy nhiên, trong một số trường hợp, view có thể được đại diện bởi một mẫu dữ liệu (data template) xác định các thành phần giao diện (UI elements) được sử dụng để đại diện cho một đối tượng khi nó được hiển thị. Bằng việc sử dụng các mẫu dữ liệu (data template), người thiết kế (designer) có thể định nghĩa cách một view model được vẽ ra (render) như thế nào hoặc có thể chỉnh sửa phần mặc định của nó mà không phải thay đổi đối tượng bên dưới, hoặc hành vi của control được sử dụng để hiển thị.

Những mấu dữ liệu (data template) có thể xem như là các view mà không có phần code-behind. Chúng được thiết kế để kết buộc cho một kiểu view model xác định bất kể khi nào một view model được yêu cầu hiển thị ra giao diện bên ngoài. Vào thời điểm thực thi, view, đã được định nghĩa bởi mẫu dữ liệu, sẽ tự động được tạo và data context của nó được đưa vào view model tương ứng.

Trong WPF, bạn có thể liên kết một mẫu dữ liệu với một kiểu view model ở mức ứng dụng (application level). WPF sau đó sẽ tự động đưa mẫu dữ liệu này vào bất kì đối tượng view model của kiểu đã được định nghĩa bất kể khi nào chúng được hiển thị trên giao diện. Trong Silverlight, bạn phải định nghĩa rõ ràng mẫu dữ liệu cho một đối tượng view model bên trong control dùng để hiển thị nó.

Tóm lại, view có những đặc trưng chính sau:

– View là một thành phần tể hiện, như là cửa số window, page, user control hoặc mẫu dữ liệu. View định nghĩa những control được chứa trong view và khung hiển thị và kiểu của chúng.

– View tham chiếu view model thông qua thuộc tính DataContext của nó. Những control trong view là dữ liệu ràng buộc các thuộc tính và dòng lệnh được thực thi bởi view model.

– View có thể điều chính việc kết buộc dữ liệu giữa view và view model. Ví dụ, view có thể sử dụng các bộ chuyển đổi giá trị để định dạng dữ liệu được hiển thị trên giao diện, hoặc nó có thể sử dụng luật kiểm tra (validation rules) để kiểm tra dữ liệu người dùng nhập vào.

– View xác định và xử lý các hành vi (behavior) như là chuyển động hoặc chuyển tiếp được kích hoạt từ một sự thay đổi trạng thái trong view model hoặc thông qua sự tương tác giữa người dùng và giao diện.

– Code-behind của view có thể định nghĩa lớp giao diện để thực thi

ViewModel Class

ViewModel trong MVVM thực hiện việc tham chiếu các các” đối tượng” cần thiết cho View từ presentation logic và data. Không có bất kì tham chiếu trực tiếp nào đến View hoặc bất kì thông tin gì về việc implement cụ thể của View. ViewModel thực thi các câu lệnh và các thuộc tính mà View có thể kết buộc dữ liệu được và thông báo về sự thay đổi trạng thái cho View thông qua các sự kiện. Các câu lệnh và thuộc tính mà ViewModel cung cấp xác định chức năng được đề nghị bởi UI, nhưng View sẽ xác định cách chức năng đó được render như thế nào.

ViewModel chịu trách nhiệm phối hợp tương tác của View với các model class được yêu cầu. Thông thường, có một mối quan hệ 1-nhiều giữa các lớp các lớp ViewModel và Model. View có thể chọn để đưa ra các model class trực tiếp đến View để các control trong View có thể kết buộc dữ liệu trực tiếp đến chúng. Trong trường hợp này, các model class cần được thiết kế để hỗ trợ việc kết bôộc dữ liệu và các sự kiện thông báo sự thay đổi có liên quan.

ViewModel có thể chuyển đổi hoặc tính toán model data để nó có thể được View sử dụng dễ dàng. ViewModel có thể xác định các thuộc tính boe63 sung để hỗ trợ đặc biệt cho View; những thuộc tính này có thể được thêm vào một cách linh hoạt. Ví dụ: ViewModel có thể giá trị của hai trường để View có thể dễ dàng thể hiện hơn, hoặc nó có thể tính số kí tự còn lại trong phần input đối với những trường có độ dài kí tự giới hạn. ViewModel cũng có thể thực thi việc kiểm tra dữ liệu bnan đầu (data validation logic) để đảm bảo tính nhất quán của dữ liệu.

ViewModel cũng có thể xác định trạng thái logic mà View có thể sử dụng để đưa ra những sự thay đổi đồ họa trong UI. View có thể xác định những thay đổi về layout hoặc style có thể ảnh hưởng đến trạng thái của ViewModel. Thông thường, ViewModel sẽ xác định các câu lệnh và hành động có thể được mô tả trong UI và người dùng có thể thực thi. Một ví dụ phổ biến đó là khi ViewModel cung cấp câu lệnh Submit cho phép người dùng gửi dữ liệu đến một web service hoặc một nơi lưu trữ dữ liệu. View có thể chọn để mô tả câu lệnh bằng một nút nhấn (button) để người dùng có thể nhấn vào để gửi dữ liệu đi. Thông thường, khi câu lệnh ở trạng thái “unavailable”, các thành phần UI có liên quan của nó cũng bị vô hiệu hóa. Các câu lệnh là một cách để đóng gói các hành động của người dùng và tách biệt rõ ràng chúng khỏi phần UI.

Tóm lại, ViewModel có những đặc điểm chính sau:

– ViewModel không phải là lớp có tính chất đồ họa (non-visual class) và không chuyển hóa từ WPF hay Silverlight base class. Nó đóng gói phần representaion logic được yêu cầu để hỗ trợ cho một use case hoặc user task trong ứng dụng. ViewModel có thể kiểm thử độc lập với View và Model.

– Thông thường ViewModel không tham chiếu trực tiếp đến View. Nó thực thi các thuộc tính và câu lệnh để View có thể kết buộc dữ liệu. Nó thông báo cho View khi có bất kì sự thay đổi trạng thái nào thông qua các sự kiện (thông qua các interface INotifyPropertyChangedINotifyCollectionChaged).

– ViewModel phối hợp sự tương tác của View với Model. Nó có thể chuyển đổi hoặc tính toán dữ liệu để nó có thể dễ dàng được sử dụng bởi View và có thể thực thi các thuộc tính bổ sung mà không thể mô tả trên Model. Nó cũng có thể thực thi việc kiểm tra dữ liệu thông qua các interface IDataErrorInfo hoặc INotifyDataError.

– ViewModel có thể xác định các trạng thái logic mà View có thể mô tả cho người dùng.

Model Class

Model trong MVVM đóng gói business logic và data. Business logic được định nghĩa như bất kì application logic nào liên quan đến việc lấy và quản lý dữ liệu của ứng dụng và đảm bảo rằng bất kì business rule nào mà đảm bảo sự thống nhất của dữ liệu và việc kiểm tra chúng là bắt buộc. Để tối ưu hóa việc tái sử dụng, các model không nên chứa bất kì use case-specific hoặc user task-specific behavior hoặc application logic nào.

Thông thường, Model mô tả client-side domain model cho ứng dụng. Nó có thể định nghĩa các cấu trúc dữ liệu dựa trên data model của ứng dụng và bất kì business và valication logic nào hỗ trợ. Model cũng có thể bao gồm code để hỗ trợ việc truy xuất dữ liệu và caching, Thông thường, Model và data access layer được tạo ra như một phần của một data access hoặc service strategy, chẳng hạn như ADO.NET Entity Framework, WCF Data Service, hoặc WCF RIA Service.

Thông thường, Model thực thi các thành phần mà nó có thể dễ dàng kết buộc cho View. Điều này có nghĩa là nó hỗ trợ thông báo thuộc tính và collection bị thay đổi thông qua các interface INotifyPropertyChangedINotifyCollectionChanged. Thông thường các model class mô tả các collection của các đối tượng chuyển hóa từ lớp ObservableCollection<T> cung cấp việc thực thi INotifyCollectionChanged interface.

Model cũng có thể hỗ trợ việc kiểm tra dữ liệu và thông báo lỗi thông qua các interface IDataErrorInfo (hoặc INotifyDataErroInfo). Những interface này cho phép việc kết buộc dữ liệu của WPF và Silverlight được thông báo khi các giá trị thay đổi để UI có thể được cập nhật. Chúng cũng hỗ trợ việc kiểm tra dữ liệu và thông báo lỗi trong UI layer.

Tóm lại, Model có những đặc điểm chính sau:

– Các model class là những lớp non-visual đóng gói dữ liệu và business logic của ứng dụng. Chúng chịu trách nhiệm quản lý dữ liệu của ứng dụng và đảm bảo tính thống nhất của nó và kiểm tra bằng việc đóng gói các business rule và data valication logic được yêu cầu.

– Các model class không tham chiếu trực tiếp đến View và các ViewModel class và không có sự phụ thuộc vào cách chúng được thực thi.

– Thông thường các model class cung cấp các sự kiện thông báo thay đổi thuộc tính và collection thông qua các interface INotifyPropertyChangedINotifyCollectionChanged. Điều này cho phép chúng dễ dàng kiểm soát dữ liệu trong View. Các model class mô tả các tập hợp của các đối tượng thường chuyển hóa từ lớp ObservableCollection<T>.

– Thông thường các model class cung cấp việc kiểm tra dữ liệu và thông báo lỗi thông qua các interface IDataErrorInfo hoặc INotifyDataErrorInfo

– Thông thường các model class được sử dụng trong việc kết hợp với một dịch vụ hoặc nơi lưu trữ thực hiện việc truy xuất dữ liệu và caching.

 Sự tương tác giữa các lớp

Mẫu thiết kế MVVM đưa ra cách tách biệt rõ rệt giữa UI, presentation logic, business logic và dữ liệu của ứng dụng bằng cách tách thành các lớp riêng biệt. Các tương tác giữa View và ViewModel là phần quan trọng nhất để tìm hiểu, nhưng các tương tác giữa các model class và ViewModel cũng quan trọng không kém. Nội dung trình bày sau đây mô tả các pattern khác cho các tương tác này và mô tả cách thiết kế chúng khi bạn thực thi mẫu thiết kế MVVM trong các ứng dụng của bạn.

P/s: trong bài viết kế tiếp sẽ đi sâu vào phần thực thi mẫu thiết kế MVVM như thế nào, khi đó các bạn sẽ nắm rõ hơn cách thức hoạt động bên trong của mẫu thiết kế này, đồng thời sẽ có demo ứng dụng mẫu để các bạn dễ hình dung ứng dụng thực tế nó như thế nào.

Xây dựng ứng dụng WPF với mẫu thiết kế MVVM

Việc xây dựng giao diện người dùng của một ứng dụng phần mềm trong thực tế là không hề dễ dàng. Nó có thể là sự “hòa trộn” không rõ ràng của dữ liệu, thiết kế tương tác, thiết kế đồ họa, sự liên kết, vấn đề bảo mật, unit testing… Có rất nhiều mẫu thiết kế thông dụng có thể giúp giải quyết phần nào vấn đề, nhưng thực sự việc tách biệt và ánh xạ nhiều mối liên hệ có thể khá khó khăn. Đôi khi chúng ta sử dụng những mẫu thiết kế phức tạp đòi hỏi phải viết code nhiều vì nền tảng giao diện người dùng (UI platform) không đáp ứng được. Điều chúng ta cần là một nền tảng (platform) giúp xây dựng những giao diện (UI) bằng cách sử dụng những mẫu thiết kế đơn giản, có khả năng kiểm thử theo thời gian, có thiên hướng nghiêng về phía lập trình. Và Windows Presentation Foundation (WPF) cung cấp đúng những gì chúng ta cần.

Quá trình hình thành Model-View-ViewModel

Từ khi con người bắt đầu tạo ra các giao diện người dùng của phần mềm, có rất nhiều mẫu thiết kế phổ biến giúp việc này trở nên đơn giản hơn. Ví dụ, mẫu thiết kế Model-View-Presenter (MVP) trở nên phổ biến trong nhiều nền tảng lập trình giao diện người dùng khác nhau. MVP là một biến thể của mẫu thiết kế Model-View-Controller (MVC) được dùng trong vòng vài thập kỉ trở lại đây. Trong trường hợp bạn chưa từng sử dụng mô hình MVP trước đó thì sau đây là một trình bày ngắn gọn về mô hình này: những gì bạn thấy trên màn hình được gọi là View, dữ liệu mà nó hiển thị lên gọi là Model và Presenter “móc” 2 phần kia lại với nhau. View phụ thuộc vào Presenter để lấy dữ liệu, tương tác với dữ liệu đầu vào, cung cấp các phép kiểm tra dữ liệu đầu vào đó trước khi xử lý chúng và nhiều tác vụ khác nữa.

Vào năm 2004, Martin Fowler đã phát hành một bài viết có tựa đề Presentation Model (PM). Mẫu thiết kế PM này tương tự với MVP trong việc tách biệt View từ hành vi (behavior) và trạng thái (state). Phần thú vị của mẫu thiết kế này là phần trừu tượng của View được tạo ra, gọi là Presentation Model. Theo Fowler, Presentation Model thường xuyên cập nhật View của nó để cả hai luôn đồng bộ với nhau.

Vào năm 2005, John Gossman, hiện tại là trong những kiến trúc sư về WPF và Silverlight tại Microsoft, đã công bố mẫu thiết kế Model-View-ViewModel (MVVM) trên blog của mình. MVVM gần như giống hoàn toàn với PM của Fowler khi cả hai mẫu thiết kế mô tả một sự trừu tượng của View chứa hành vi và trạng thái của View. Fowler đã giới thiệu PM với ý nghĩa là tạo ra một lớp trừu tượng độc lập với nền tảng giao diện của View, trong khi đó Gossman giới thiệu MVVM như là một cách được chuẩn hóa để tận dụng những tính năng cốt lõi của WPF để đơng giản hóa việc tạo ra những giao diện người dùng.

Trong bài viết của Glenn Block có tựa đề “Prism: Patterns for Building Composite Applications with WPF” trong ấn bản số ra vào tháng 9/2008 của tạp chí MSDN, Glenn hướng dẫn xây dựng ứng dụng composite cho WPF. Khái niệm ViewModel không được đề cập đến trong toàn bộ bài viết. Thay vào đó, khái niệm Presentation Model được sử dụng để mô tả phần trừu tượng của View.

MVVM_architecture

Không giống như Presenter trong mô hình MVP, ViewModel không cần tham chiếu đến View. View kết buộc các thuộc tính trên ViewModel lần lượt thể hiện dữ liệu được lưu trữ trong các đối tượng mẫu (model object) và các trạng thái khác xác định cho View. Những kết buộc giữa View và ViewModel khá đơn giản để xây dựng vì một đối tượng ViewModel được thiế lập như một DataContext của một View. Nếu các giá trị thuộc tính trong ViewModel thay đổi, những giá trị thuộc tính mới tự động phát sinh sang View thông qua việc kết buộc dữ liệu. Khi một người dùng nhấn một nút trong View, một lệnh trong ViewModel được thực thi để thực hiện hành động được yêu cầu. ViewModel, chứ không phải View, thực hiện tất cả những phần sửa đổi được tạo ra cho dữ liệu mẫu (model data). Các lớp View không nghĩa rằng các lớp model tồn tại, trong khi ViewModel và model không hề biết View. Thực tế, model hoàn toàn không biết sự thật là View và ViewModel tồn tại.

Tại sao lập trình viên WPF thích dùng MVVM

Khi một lập trình viên trở nên quen thuộc với WPF và MVVM, và thật khó để phân biệt cả hai. MVVM hoàn toàn phù hợp với nền tảng WPF, và WPF được thiết kế để giúp cho việc xây dựng các ứng dụng sử dụng mô hình MVVM. Thực tế, Microsoft đã sử dụng MVVM bên trong để phát triển các ứng dụng WPF như là Expression Blend, trong khi phần nền tảng lõi WPF đã đang được xây dựng. Nhiều khía cạnh của WPF như là look-less control model và những mẫu dữ liệu (data templates), tận dụng điểm mạnh trong việc tách biệt phần hiển thị từ hành vi và trạng thái được đưa ra bởi MVVM.

Một khía cạnh quan trọng nhất của WPF làm cho MVVM trở thành mẫu thiết kế tốt để dùng là phần kết cấu kết buộc dữ liệu (data binding infrastructure). Bằng việc kết buộc các thuộc tính của View sang ViewModel, bạn có kết nối “lỏng” giữa View và ViewModel và bỏ hoàn toàn việc cần thiết phải viết code trong ViewModel để cập nhật trực tiếp View. Hệ thống kết buộc dữ liệu cũng hỗ trợ việc kiểm tra nguồn đầu vào quy định một cách được chuẩn hóa trong việc đưa ra các lỗi (validation errors) cho View.

Hai tính năng khác của WPF làm cho mẫu thiết kế này dễ sử dụng là các mẫu dữ liệu (data templates) và hệ thống tài nguyên (resource system). Những mẫu dữ liệu gắn các View sang các đối tượng ViewModel được hiển thị trong giao diện người dùng. Bạn có thể định nghĩa các template trong XAML và để hệ thống tài nguyên tự động định vị và áp dụng các mẫu này cho bạn vào thời điểm hoạt động (runtime). Bạn có thể tìm hiểu thêm về việc kết buộc và các mẫu dữ liệu trong bài viết này.

Ngoài các tính năng của WPF (và Silverlight) làm cho MVVM trở thành cách tự nhiên để xây dựng một ứng dụng, mẫu thiết kế này cũng rất phổ biến vì các lớp ViewModel dễ dàng để kiểm thử mức đơn vị (unit test). Khi phần tương tác của một ứng dụng nằm trong tập các lớp ViewModel, bạn có hể dễ dảng viết code để kiểm tra nó. Về mặt ý nghĩa nào đó, View và unit test là hai loại khác nhau của ViewModel. Ngoài ý nghĩ thúc đẩy việc tạo ra những phần kiểm tra hồi quy tự động (automated regression tests), khả năng kiểm thử của những lớp ViewModel có thể hỗ trợ trong việc thiết kế các giao diện người dùng. Khi bạn đang thiết kế một ứng dụng, bạn có thể quyết định những gì nên có trong View hoặc ViewModel bằng việc tưởng tượng rằng bạn có thể viết unit test cho ViewModel. Nếu bạn cò thể viết unit test cho ViewModel mà không cần phải tạo ra bất kì đối tượng UI nào, bạn cũng hoàn toàn có thể giới hạn ViewModel vì nó không phụ thuộc vào các thành phần hiển thị.

Cuối cùng, đối với những lập trình viên làm việc với những ngươi thiết kế đồ họa, việc sử dụng MVVM giúp dễ dàng hơn trong việc tạo một quy trình mềm dẻo giữa lập trình viên và đồ họa viên. Nhóm phát triển có thể tập trung vào việc tạo các lớp ViewModel, và nhóm thiết kế có thể tập trung vào việc tạo ra các View thân thiện với người dùng. Việc kết nối phần đầu ra của cả ai nhóm có thể ít liên quan hơn so với việc đảm bảo các kết buộc chính xác trong tập tin XAML của View.