Thursday, 12 April 2012

CHƯƠNG III: TẠO VÀ MỞ RỘNG MỘT ĐỐI TƯỢNG VỚI MẪU DECORATOR VÀ FACTORY

copyright from: diendan.congdongcviet.com


Vậy là bạn đã bao bọc đối tượng computer, và khi bạn gọi phương thức description của đối tượng disk này, nó sẽ gọi phương thức description của lớp computer, đồng thời thêm vào dòng chữ “and a disk”. Kết quả bạn sẽ có “computer and a disk”

Thêm vào một ổ CD

Bạn cũng có thể thêm vào một ổ CD theo cùng cách trên. Đây là mã nguồn



Thêm vào một màn hình monitor

Tất nhiên bạn cũng có thêm vào một màn hình theo cùng một cách như sau:



OK. Bạn đã có đầy đủ các lớp. Giờ là lúc chạy thử nghiệm chương trình.

Đầu tiên bạn tạo đối tượng computer như sau:



Sau đó bạn bao bọc đối tượng computer để thêm vào một đĩa cứng



Bây giờ hãy thêm vào một monitor:



Sau đó, bạn có thêm vào không chỉ một ổ CD, mà là hai ổ CD chẳng hạn. Không vấn đề gì khó khăn cả. Cuối cùng bạn gọi phương thức description của lớp bao bọc để xem kết quả:



OK. Khi chạy chương trình bạn nhận được kết quả.



Không tồi. Bạn đã mở rộng một đối tượng gốc thật đơn giản bằng cách bao bọc nó trong nhiều lớp trang trí decorator khác nhau, tránh việc phải chỉnh sửa trong mã nguồn gốc. Và đó là Mẫu Thiết Kế Trang Trí Decorator.

CẢI TIẾN TOÁN TỬ NEW VỚI MẪU THIẾT KẾ NHÀ MÁY FACTORY

Tại đây, công ty MegaGigaCo, bạn được trả giá cao cho kỹ năng thiết kế mẫu chuyên nghiệp của mình, bạn đang tạo một đối tượng kết nối cơ sở dữ liệu mới. Hãy xem toán tử new trong Java làm việc này như thế nào?



“Không tồi,” bạn nghĩ, sau khi hoàn thành đoạn mã việc lớp OracleConnection. Bây giờ bạn đã có thể kết nối với cơ sở dữ liệu Oracle.

“Nhưng,” Giám đốc điều hành la lên ,”làm thế nào để kết với máy chủ cơ sở dữ liệu Microsof SQL Server?”

“Được”, bạn nói “Bình tĩnh, để tôi suy nghĩ một lát”. Bạn ra khỏi phòng để ăn trưa và sau đó quay lại tìm giám đốc và ban quản trị. Mọi người nóng lòng chờ đợi và hỏi “Mọi việc đã xong chưa?”

Bạn trở lại làm việc và tạo ra một lớp mới dùng để kết nối cơ sở dữ liệu, lớp SQLServerConnection.



“Tốt lắm” Vị giám đốc nói. “Umh, vậy làm sao để kết nối với MySQL? Chúng ta muốn nó là kết nối mặc định”. “Woa”, bạn hơi bối rối. Tuy nhiên bạn vẫn làm thêm một kết nối với MySQL như sau:



Hiện tại bạn đã có ba loại kết nối cơ sở dữ liệu như sau: Oracle, SQL Server và MySQL. Vì vậy bạn chỉnh sửa mã nguồn cho phù hợp với các biến như “Oracle”, “SQL Server” hay bất cứ biến nào khác như sau:



Mọi việc đều ổn, bạn nghĩ. Tuy nhiên có tới 200 chỗ trong mã nguồn cần phải tạo kết nối cơ sở dữ liệu. Vì vậy đã tới lúc đưa đoạn mã này vào một phương thức riêng, phương thức createConnection, qua truyền cho nó loại kết nối mà bạn muốn như sau:



Phương thức có thể trả về loại kết nối mong muốn, tùy thuộc vào giá trị tham số truyền vào:



Tuyệt, bạn nghĩ. Không có gì khó khăn ở đây. 

“Tin xấu” Vị giám đốc nói lớn trong khi chạy ào vào phòng làm việc của bạn. “Chúng ta cần phải chỉnh sửa lại mã nguồn để xử lý các kết nối an toàn cho tất cả máy chủ cơ sở dữ liệu. Hội đồng quản trị của khu vực Western yêu cầu như vậy”

Bạn đưa vị giám đốc ra khỏi phòng và ngồi suy nghĩ. Tất cả mã nguồn chắc phải chỉnh sửa lại. Phương thức mới createConnection, phần chính của mã nguồn, sẽ phải chỉnh sửa lại.

Trong chương II của quyển sách này. Bạn đã được biết dấu hiệu phải sử dụng mẫu thiết kế: “Đó là tách rời phần mã nguồn dễ thay đổi nhất ra khỏi phần mã chính của bạn. Và cố gắng sử dụng lại những phần này càng nhiều càng tốt.” 

Có lẽ đây là lúc nghĩ về việc tách rời phần mã nguồn dễ thay đổi ra khỏi chương trình chính, phần tạo kết nối cơ sở dữ liệu connection, và đóng gói nó vào một đối tượng. Và đối tượng đó chính là mẫu nhà máy Factory. Đối tượng là một nhà máy, được viết trong mã nguồn, nhằm tạo ra các đối tượng kết nối connection.

Vì sao bạn nghĩ tới mẫu thiết kế nhà máy Factory. Đây là những gợi ý:
  • Bạn sử dụng toán tử new để tạo đối tượng OracleConnection
  • Sau đó lại sử dụng tiếp toán tử new để tạo đối tượng SQLServerConnection, và sau đó là MySQLConnection. Nói cách khác, bạn đã sử dụng toán tử new để tạo nhiều đối tượng thuộc các lớp khác nhau, điều này làm mã nguồn của bạn trở nên lớn hơn và bạn buộc phải lặp lại điều này nhiều lần trong toàn bộ mã nguồn.
  • Sau đó bạn đưa đoạn mã đó vào trong một phương thức
  • Bởi vì yêu cầu vẫn còn có thể thay đổi nhanh chóng, nên cách tốt nhất là đóng gói chúng vào một đối tượng nhà máy factory. Theo cách làm này, bạn đã tách phần mã dễ thay đổi riêng biệt ra và giúp phần mã nguồn còn lại giữ vững nguyên tắc “đóng cho việc sửa đổi”

Chúng ta có thể nói rằng, toán tử new vẫn tốt trong mọi trường hợp, nhưng khi mã tạo dựng đối tượng bị liên tục thay đổi, ta nên nghĩ đến việc đóng gói chúng bằng mẫu thiết kế nhà máy factory.

XÂY DỰNG MẪU NHÀ MÁY FACTORY ĐẦU TIÊN

Nhiều lập trình viên biết cách thức mà đối tượng nhà máy factory làm việc. Họ nghĩ đơn giản rằng, bạn có một đối tượng làm nhiệm vụ tạo ra đối tượng khác. Đó là cách mà đối tượng factory thường được tạo ra và sử dụng, tuy nhiên nó còn làm được nhiều hơn thế. Chúng ta hãy nhìn vào cách thông thường khi tạo một đối tượng nhà máy factory trước, sau đó xem xét định nghĩa chính xác từ sách của GOF, theo định nghĩa mà đối tượng nhà máy factory sẽ có nhiều điểm khác, nhiều sự uyển chuyển hơn.

Tạo dựng đối tượng nhà máy Factory

Ví dụ đầu tiên, FirstFactory, sẽ làm việc theo cách hiểu thông thường nhất. Lớp FirstFactory đóng gói đối tượng xây dựng connection, và bạn truyền giá trị tham số theo đúng loại muốn tạo đó là “Oracle” hay “SQL Server” hay loại gì khác. Đây là cách bạn tạo một đối tượng sử dụng nhà máy factory :



Bây giờ, bạn có thể sử dụng đối tượng nhà máy factory mới tạo này, để tạo đối tượng kết nối connection, bằng cách gọi phương thức tên createConnection như sau:



Vậy bạn đã tạo lớp nhà máy FirstFactory như thế nào? Hãy xem mã sau:



Đầu tiên bạn truyền kiểu kết nối vào phương thức khởi tạo của lớp FirstFactory.

Lớp FirstFactory chứa đựng một phương thức createConnection dùng để tạo ra một đối tượng kết nối connection thật sự. Đây là nơi bạn phải chỉnh sửa mã nguồn nhiều nhất tùy theo loại kết nối muốn tạo, mã như sau:



Kết quả, bạn đã có một lớp nhà máy factory.

Tạo một lớp kết nối Connection trừu tượng

Hãy nhớ rằng một trong những mục tiêu của chúng ta khi viết mã, là làm sao việc thay đổi phần chính của mã nguồn càng ít càng tốt. Với mục tiêu đó, hãy nhìn đoạn mã sau làm việc, khi ta sử dụng một đối tượng connection được tạo bởi đối tượng nhà máy factory:



Bạn có thể thấy rằng, đối tượng kết nối connection được tạo bởi nhà máy factory, được sử dụng khắp nơi trong mã nguồn. Để sử dụng cùng một đoạn mã cho tất cả các loại kết nối khác nhau (Oracle,MySQL...), đoạn mã cần phải được viết theo tính “đa hình”, có nghĩa là tất cả các đối tượng connection, đều có cùng một giao diện interface, hay cùng kế thừa từ một lớp cơ sở. Theo cách đó, bạn có thể sử dụng cùng một biến cho mọi loại đối tượng kết nối.

Trong ví dụ, tôi tạo một lớp trừu tượng connection, để các lớp khác kế thừa nó. Lớp này gồm một phương thức khởi dựng, và một phương thức description ( trả về mô tả của loại đối tượng ). Mã như sau:



OK. Mọi việc có vẻ tốt đẹp. Bây giờ bạn đã tạo một lớp trừu tượng cơ sở cho các lớp kết nối khác kế thừa. Bạn cần phải kế thừa tất cả các đối tượng kết nối connection tạo ra từ lớp nhà máy factory. 

Tạo lớp kế nối connection

Có ba lớp kết nối connection mà nhà máy Factory có thể tạo ra, phù hợp với loại kết nối mà Vị giám đốc mong muốn: OracleConnection,SqlServerConnection,MySQLConnecti on. Như chúng ta vừa nói, cần phải kế thừa từ lớp trừu tượng vừa tạo. Và mỗi loại trong chúng đều có phương thức decription trả về mô tả của từng loại kết nối một. Đây là mã nguồn của lớp OracleConnection:



Đây là lớp SqlServerConnection, cũng kế thừa từ lớp trừu tượng Connection:



Và lớp MySqlConnection cũng tương tự:



Tuyệt với. Mọi việc hoàn tất. Giờ là lúc thử nghiệm chúng. Đầu tiên ta tạo lớp nhà máy, truyền tham số khởi dựng là Oracle:





Để kiểm tra lại đối tượng connection được tạo có phải là Oracle không, ta gọi phương thức description như sau:



Kết quả bạn nhận được



Không tồi. Đó là những gì bạn mong đợi.

Ghi nhớ:Theo sách GoF, mẫu thiết kế phương thức nhà máy Factory Method được định nghĩa “Định nghĩa một giao diện để tạo một đối tượng, nhưng cho phép các lớp con quyết định cách thức thể hiện nó. Phương thức nhà máy Factory cho phép một lớp trì hoãn việc hiện thực của nó qua các lớp con”

Điểm mấu chốt ở đây là phần “để lớp con quyết định”. Cho tới bây giờ, lớp nhà máy Factory mà bạn vừa tạo, vẫn chưa cho phép các lớp con quyết định cách thể hiện, trừ việc cho kế thừa và ghi đè lại phương thức của lớp Connection cơ sở.

Mẫu thiết kế phương thức nhà máy Factory Method của GoF đem đến cho bạn khả năng uyển chuyển hơn phương pháp truyền thống rất nhiều. Cách làm của GoF là: bạn định nghĩa cách phương thức nhà máy Factory làm việc, và cho phép các lớp con hiện thực implement một nhà máy factory thật sự.

Chúng ta đã nói rằng, Hội đồng quản trị khu vực Western bất ngờ gọi điện và yêu cầu họ không thích lớp nhà máy FirstFactory, họ muốn có thể tạo ra các kết nối bảo mật đến máy chủ cơ sở dữ liệu, không chỉ là một kết nối thông thường. Điều này có nghĩa là họ phải viết lại lớp nhà máy FirstFactory mỗi khi bạn thay đổi nó, để họ có thể tạo ra một kết nối bảo mật.

Đây là vấn đề của các lập trình viên. Mỗi khi bạn cập nhật lại lớp FirstFactory, các lập trình viên khác phải viết lại mã của họ để thích hợp với yêu cầu của họ. Họ đang gọi và yêu cầu rằng họ muốn kiểm soát được quá trình nhiều hơn.

Tốt thôi, bạn nói. Đó chính là vấn đề mẫu thiết kế Factory áp dụng, giao quyền kiểm soát cho các lớp con. Để thấy cách mẫu này hoạt động, bạn thay đổi cách tạo đối tượng kết nối connection, sử dụng kỹ thuật của GoF, bạn sẽ làm cho khu vực Western của công ty MegaGigaCo hài lòng.

Gợi ý: Bạn vẫn còn băn khoăn về cách sử dụng của mẫu nhà máy Factory của GoF? Mẫu Factory được sử dụng khi bạn muốn chuyển giao toàn bộ quyền điều khiển các lớp con cho các lập trình viên khác.

TẠO MỘT NHÀ MÁY FACTORY THEO CÁCH CỦA GoF

Làm cách nào để “cho phép các lớp con toàn quyền hiện thực cách lớp con thể hiện” khi tạo một đối tượng nhà máy factory. Cách mà bạn phải làm là định nghĩa lớp nhà máy factory như là một lớp trừu tượng abstract hay giao diện interface, và để cho các lớp con hiện thực implement nó.
Nói cách khác, bạn tạo ra một khung sườn cho lớp nhà máy Factory tại trụ sở của MegaGigaCo, và việc hiện thực lớp này sẽ do các lớp con đảm nhiệm.

Tạo lớp nhà máy trừu tượng factory

Việc tạo lớp trừu tượng factory rất dễ dàng. Lớp này được gọi là ConnectionFactory



Bên cạnh một phương thức khởi dựng rỗng, phương thức quan trọng nhất ở đây là phương thức nhà máy createConnection. Ta phải làm cho phương thức mang tính trừu tượng, để các lớp con hiện thực nó. Phương thức này nhận một đối số, đó là loại kết nối cần tạo:



Và đó là tất cả những gì bạn cần. Sự đặc tả cho đối tượng nhà máy factory. Bây giờ khu vựa Western sẽ hài lòng vì họ có thể hiện thực một đối tượng nhà máy cụ thể thích hợp với họ từ lớp trừu tượng trên.

Tạo một lớp nhà máy factory cụ thể

Bạn đã bay tới khu vực Western của công ty MegaGigaCo, để giúp họ xử lý vấn đề tạo đối tượng. Bạn giải thích ”Tôi hiểu rằng các anh muốn được quyền điều khiển nhiều hơn đối với các đối tượng kết nối”

“Vâng” các lập trình viên của Western nói. “Chúng tôi muốn có thể làm việc với các kết nối bảo mật. Chúng tôi đã tạo một vài lớp mới, lớp SecureOracleConnection, SecureSqlServerConnection và SecureMySqlConnection để tạo ra các kết nối bảo mật.

“OK” bạn nói. “tất cả những gì các bạn phải làm là mở rộng lớp trừu tượng mới của tôi, tên là ConnectionFactory khi các bạn muốn tạo đối tượng nhà máy factory cho các bạn. Hãy chắc chắn là các bạn sẽ hiện thực phương thức createConnection. Sau đó bạn có thể tùy ý viết mã cho phương thức createConnection để tạo đối tượng theo đúng cách bảo mật mà bạn muốn”

Các lập trình viên của Western nói. “Wa, thật dễ dàng. Chúng tôi sẽ tạo lớp factory mới với tên SecureFactory, và nó sẽ kế thừa từ ConnectionFactory như sau:



“Tiếp theo,” các lập viên của khu vực Western nói “Chúng chỉ cần hiện thực lớp createConnection mà lớp trừu tượng ConnectionFactory yêu cầu:



“Cuối cùng,” các lập trình viên nói “chúng ta chỉ cần tạo các đối tượng từ các lớp vừa tạo, lớp SecureOracleConnection, SecureSqlServerConnection và SecureMySqlConnection, tùy thuộc vào kiểu dữ liệu được truyền vào hàm createConnection:



“Thật đơn giản” Họ nói.

Sự khác biệt giữa cách tạo mẫu nhà máy factory thông thường và cách của GoF là cách của GoF chỉ đặc tả lớp nhà máy factory và để cho các lớp con xử lý nội dung chi tiết.

Tạo các lớp kết nối bảo mật

Để hiểu rõ cách thức GoF tạo mẫu factory, bạn cần tạo các lớp cụ thể cho đối tượng nhà máy connect mới, lớp SecureOracleConnection, SecureSqlServerConnection và lớp SecureMySqlConnection. Thật dễ dàng để tạo chúng ,bắt đầu từ lớp SecureOracleConnection, với hàm description trả về văn bản “Oracle Secure”:



Tiếp theo là lớp SecureSqlServerConnection, với hàm description trả về văn bản “SQL Server Secure”



Và lớp SecureMySqlConnection, với hàm description trả về văn bản “MySql Secure”:



Vậy là đã hoàn tất phần mã nguồn. Giờ là lúc cho chương trình chạy.

Thực thi chương trình

Để kiểm tra mã nguồn, hãy tạo đối tượng SecureFactory và sử dụng nó để tạo đối tượng SecureOracleConnection. Mã như sau:



Tất cả những gì bạn cần phải làm là sử dụng hàm createConnection của đối tượng nhà máy factory để tạo các kết nối an toàn. Mã như sau:



Khi chạy chương trình, đúng như mong đợi, bạn nhận được văn bản sau, chứng tỏ rằng bạn đang sử dụng một kết nối Oracle bảo mật:



Đây cũng là kết quả mà bạn nhận được từ ví dụ FirstFactory mà chúng ta đã nói trong phần trước, ngoại trừ một điều là bạn cho phép khu vựa Western tự mình hiện thực loại nhà máy factory mà họ mong muốn. Bạn đặc tả một lớp nhà máy bằng cách tạo ra một lớp trừu tượng hay một giao diện interface để các lớp con sử dụng, và người khác sẽ tự mình quyết định lớp đó thực hiện như thế nào. Không còn việc sử dụng một đối tượng nhà máy cụ thể, nay tập hợp các lớp con quyết định việc thể hiện chúng như thế nào.

Monday, 9 August 2010

Kiến trúc Model-View-Presenter trong .NET

Khảo sát mẫu kiến trúc MVC cổ điển và một mẫu kiến trúc thừa kế từ nó hiện được ứng dụng rộng rãi trong môi trường .NET, đó là kiến trúc MVP.
Trong các nền tảng lập trình hiện đại như .NET, khi mà các công cụ và kĩ thuật hỗ trợ lập trình giao diện người dùng (UI) ngày càng trở nên mạnh mẽ và tiện dụng thì chúng ta thường có xu hướng đưa nhiều xử lý bên ngoài vào các lớp UI. Kết quả là các thành phần UI này chứa nhiều xử lý logic và dữ liệu mà lẽ ra nên tách rời thành những thành phần riêng. Việc tách rời xử lý và trình bày vì những lí do sau:
Trong một hệ thống, UI là thành phần có nhiều khả năng thay đổi nhất nên việc tách rời các thành phần UI giúp có thể thay đổi các thành phần UI này một cách độc lập mà không ảnh hưởng đến các thành phần xử lý khác. Đặc biệt việc tách rời các thành phần dữ liệu khỏi UI còn cho phép trên cùng một thành phần dữ liệu này, ta có thể phát triển nhiều thành phần UI khác nhau cho các cách trình bày khác nhau.
Các code xử lý đặt trong các thành phần UI còn gây khó khăn cho việc testing (unit-test). Việc test các thành phần UI thường hoặc phải chạy ứng dụng thủ công hoặc sử dụng các runner script để thực hiện việc tương tác tự động lên các thành phần UI, do đó sẽ tốn nhiều chi phí và thời gian hơn để kiểm tra các xử lý bên trong các thành phần này.
Việc đưa nhiều xử lý vào các lớp UI còn dẫn đến khả năng là trùng lắp code xử lý. Những xử lý lẽ ra có thể dùng chung thì lại xuất hiện lặp lại ở nhiều thành phần UI có chức năng tương tự nhau. Việc tách các xử lý này ra ngoài ra còn tăng tính tái sử dụng code và dễ bảo trì hơn.
Các vấn đề trên là các vấn đề cơ bản được đặt ra cho các hệ thống tương tác (interactive system). Để giải quyết chúng, một số giải pháp được đề ra trong đó phổ biến nhất là mẫu kiến trúc MVC (Model-View-Controller). Trong bài viết này, chúng ta sẽ khảo sát mẫu kiến trúc MVC cổ điển và một mẫu kiến trúc thừa kế từ nó, hiện được ứng dụng rộng rãi trong môi trường .NET, đó là kiến trúc Model-View-Presenter.

Mẫu kiến trúc Model-View-Controller cổ điển

Tổng quan
Mẫu kiến trúc Dolphin Smalltalk Model-View-Presenter chia ứng dụng thành các phần dữ liệu (data), trình bày (presentation) và các xử lý logic thuộc phần trình bày (presentation logic) thành những thành phần riêng biệt.
Lịch sử
Phiên bản Dolphin Smalltalk Model-View-Presenter (gọi tắt là Dolphin MVP) là phiên bản của MVP được xây dựng dựa trên phiên bản Taligent MVP xuất hiện trước đó. Dolphin MVP được xây dựng về cơ bản bên ngoài tương tự như MVC cổ điển nhưng khác nhau ở vai trò của Controller và Presenter.
Cấu trúc


Các thành phần
Model chứa dữ liệu và các tính toán xử lý logic để giải quyết vấn đề mà phần mềm hướng tới (business logic).
View là thành phần đảm nhận trình bày từ những dữ liệu của Model. View bao gồm những gì thể hiện trên màn hình như các control, form, widget,…
Presenter là thành phần đảm nhận các xử lý về trình bày mà nó cần đến sự tương tác trên dữ liệu.
Phối hợp các thành phần
Trong khi vai trò của Model không thay đổi so với MVC cổ điển. Thành phần View của Dolphin MVP cũng thực hiện việc trình bày từ nội dung của Model và gắn kết với Model thông qua Observer Pattern. Tuy nhiên, nó thực hiện bắt các sự kiện xảy ra ngay bên trong nó. Một vài trường hợp đơn giản như TextView, dữ liệu nhập được xử lý trực tiếp và cập nhật thay đổi trực tiếp vào Model còn hầu hết các trường hợp khác, việc xử lý được chuyển giao cho Presenter đảm trách khi cần thao tác đến Model.
Trong khi View đảm nhận trình bày thì Presenter đảm trách cách Model được thao tác và thay đổi như thế nào bởi giao diện người dùng. Presenter là nơi chứa các xử lý đặc trưng của ứng dụng (application logic so với business logic của Model). Một điểm đáng chú ý khác là Presenter có khả năng thao tác trực tiếp lên View mà nó gắn kết, điều này khác biệt với phiên bản Taligent MVP xuất hiện trước đó.
So sánh Dolphin MVP và MVC cổ điển
Điểm khác biệt cơ bản của Dolphin MVP và MVC cổ điển là sự khác nhau về vai trò của Presenter và Controller, nó cũng dẫn đến sự khác nhau về vai trò giữa hai thành phần View. Trong Dolphin MVP, sự hiện diện của Controller bị loại bỏ, thay vào đó, việc xử lý các dữ liệu input được View đảm nhận và được chuyển cho Presenter khi có yêu cầu tương tác đến Model.
Mẫu kiến trúc MVP của Martin Fowler
Tóm tắt
Martin Fowler là một trong những người đâu tiên đi sâu và nghiên cứu về các phiên bản của MVP. Vào năm 2006, Fowler tuyên bố sự kết thúc của các kiến trúc MVP cổ điển và tự chia MVP thành hai phiên bản mới là Passive View và Supervising Controller.

Martin Fowler dùng thuật ngữ Controller thay vì Presenter trong mẫu của mình
Trong mẫu Passive View, thành phần View được loại bỏ hoàn toàn các xử lý logic và tương tác đến Model. Thay vì vậy, nó chuyển giao các xử lý cho Controller đảm trách. Controller đảm nhận tương tác đến Model và cập nhật View khi có thay đổi từ Model. Controller là thành phần trung gian liên lạc giữa View và Model.
Trong mẫu Supervising Controller, View đầu tiên bắt lấy các sự kiện và sau đó chuyển giao cho controller xử lý. Để cập nhật thay đổi từ Model, View dùng data-binding và Observer pattern cho các xử lý đơn giản còn đối với các xử lý phức tạp sẽ nhờ đến Controller đảm nhận.
So với Supervising Controller chứa View đảm nhận xử lý sự kiện đơn giản thì với Passive View, thành phần View được tách rời hoàn toàn khỏi các xử lý, kể cả các xử lý cơ bản ở mức giao diện cũng được giao hoàn toàn cho Presenter xử lý. Điều này tạo thuận lợi hơn cho việc testing vì khi đó các thành phần Model và Presenter có thể được kiểm tra một cách độc lập mà không phụ thuộc vào giao diện. Phiên bản MVP của Microsoft đầu tiên được xây dựng dựa trên Passive View sau đó mẫu Supervising Controller cũng được hỗ trợ với một số điều chỉnh.
Phiên bản MVP của Microsoft
Lịch sử
Khởi đầu từ các framework Smart Client Software Factory and Web Client Software Factory dựa trên kiến trúc MVP được các nhóm nghiên cứu về pattern của Microsoft tạo ra từ năm 2006, Microsoft sau đó bắt đầu đưa MVP vào các tài liệu hướng dẫn và ví dụ về lập trình giao diện trong .NET framework. Mô tả dưới đây dựa trên phiên bản MVP được mô tả trong tạp chí MSDN vào năm 2006 (tham khảo 2) và có thể xem là kiến trúc cơ bản cho các framework trên. Ngoài ra, còn có một số framework MVP khác cũng được phát triển trên nền tảng .NET như MVC# hay NMVP.
Xin lưu ý là phiên bản cũ của Web Client Software Factory ban đầu được tổ chức dựa trên Passive View (cấu trúc bên dưới) nhưng với phiên bản hiện tại thì cấu trúc dựa trên Supervising Controller cũng được hỗ trợ.
Cấu trúc


Các thành phần
Model chứa dữ liệu và các tính toán xử lý logic để giải quyết vấn đề mà phần mềm hướng tới (business logic).
View là thành phần đảm nhận trình bày từ những dữ liệu của Model và là tổng hợp của các form, control được sử dụng.
Presenter là thành phần đảm nhận các xử lý về trình bày cũng như tương tác đến dữ liệu bên dưới và có thể tương tác để thay đổi View trong quá trình xử lý.
Phối hợp các thành phần
Mẫu kiến trúc MVP của Microsoft tương tự như Passive View nhưng nó bổ sung thêm ràng buộc là các Presenter chỉ có thể truy cập đến View thông qua các interface IView. Điều này giúp Presenter không phụ thuộc đến cài đặt cụ thể của View và có thể dễ dàng test Presenter một cách độc lập. Điều này đạt được do sử dụng IView, chúng ta có thể dùng kĩ thuật “Mock” để thay thế các xử lý cụ thể của View khi test Presenter. Ngoài ra, mối quan hệ giữa Presenter và Model là quan hệ một chiều (chiều còn lại là gián tiếp). Hệ quả của các liên kết là chúng ta có một mô hình đa lớp (multi-layer) như cấu trúc ở trên theo ý nghĩa các thành phần ở một layer chỉ phụ thuộc và sử dụng các thành phần ở layer ngay bên dưới nó.
Theo mẫu Microsoft MVP, khi một View gắn liền với một interface IView. Khi View được tạo ra, nó tạo ra một đối tượng private Presenter và gắn nó vào đối tượng này thông qua IView. Khi một sự kiện xảy ra, View bắt lấy và sau đó kích hoạt một phương thức của Presenter mà sau đó, nó có thể tương tác với Model. Một số sự kiện như bắt đầu hiển thị và đóng của View cũng được gửi tới để Presenter kích hoạt một số phương thức tương ứng, điều này giúp cho một số công việc như khởi tạo và giải phóng dữ liệu cho View được dễ dàng hơn.
Model theo Microsoft MVP cũng thường được tổ chức bổ sung một lớp Service nằm bên trên để tương tác với Presenter, qua đó giảm bớt sự phụ thuộc đến các xử lý data nằm sâu bên dưới. Ngoài ra, để điều khiển việc liên lạc giữa các View hay các Presenter, một thành phần thường được bổ sung gọi là Application Controller. Trong một ứng dụng có thể có nhiều Application Controller để quản lý các nhóm MVP. Ngoài ra, thay vì sử dụng Presenter để tương tác với Model, một cách là giao tất cả các thao tác này cho Application Controller đảm trách.
Một ví dụ minh họa đơn giản
Để minh họa mẫu Microsoft MVP, chúng ta sẽ cùng khảo sát một ví dụ đơn giản: tạo một form hiển thị danh sách sinh viên cùng chức năng thêm mới trên danh sách sinh viên đó. Với mục tiêu là minh họa cách tổ chức và liên kết giữa các thành phần là chủ yếu, chúng ta không đi sâu vào cài đặt cụ thể (bạn có thể tham khảo source code đi kèm bài viết, source code được tổ chức ở dạng project của VS 2008).
Ứng dụng được tổ chức thành 5 project: View, Presenter, DataService, Model và DataTransferObject. Trong đó, View là application còn các project khác là library. Các project phụ thuộc lẫn nhau theo thứ tự project phía trước sử dụng trực tiếp project phía sau, ngoại trừ DataTransferObject chức các loại dữ liệu được dùng chung cho 3 project View, Preseneter và DataService. Model là project chứa các tương tác trực tiếp đến dữ liệu và không phụ thuộc vào bất cứ thành phần nào khác.
Giải thích các project:
  • Model: chứa dữ liệu giả lập (StudentData.xml), schema (StudentDataSet.xsd) và lớp tiện ích được xây dựng bên trên (DataAccess.cs).
  • DataTransferObject: chứa đối tượng dữ liệu được dùng chung cho các project ở mức cao hơn (Student.cs).
  • DataService: được xây dựng bên trên Model, chuyển đổi dữ liệu từ Model sang DataTransferObject và xây dựng các tiện ích bên trên đối tượng transfer này (Service.cs).
  • Presenter: chứa interface của View dùng cho Presenter truy cập (IViewForm) và lớp Presenter (StudentPresenter).
  • View: chứa form view (ViewForm.cs) và Program.cs.
Trong bài viết khác, chúng ta sẽ tìm hiểu phương pháp TDD (Test-driven development) và ứng dụng nó để phát triển ứng dụng theo mẫu Microsoft MVP trong môi trường .NET.
Tài liệu tham khảo:
  1. Martin Fowler, GUI Architectures
  2. Jean-Paul Boodhoo , Design Patterns: Model-View-Presenter
  3. Buschmann F., Meunier R., Rohnert H. & Sommerlad P. & Stal M. (1996). Pattern-Oriented Software Architecture: A System of Patterns.
  4. Martin Fowler , Patterns of Enterprise Application Architecture.
  5.  http://en.wikipedia.org/wiki/Model_View_Presenter
  6.  http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
  7. Derek Greer, Interactive application architecture patterns
  8. Views Testability Guidance
  9. Dolphin MVP paper

Mẫu kiến trúc Model-View-Controller

Tổng quan
Mẫu kiến trúc Model-View-Controller là phương pháp chia nhỏ các các thành phần dữ liệu (data), trình bày (output) và dữ liệu nhập từ người dùng (input) thành những thành phần riêng biệt.
Lịch sử
MVC được hình thành bởi các nghiên cứu của Trygve Reenskaug vào khoảng các năm 1978-1979. Sau đó nó được điều chỉnh và được cài đặt lần đầu tiên vào các lớp của thư viện Xerox PARC Smalltalk-80. MVC cổ điển hiện tại ít được sử dụng trong môi trường lập trình desktop như trước đây nhưng hiện tại nó vẫn được sử dụng cực kì rộng rãi như là kiến trúc cơ bản trong các môi trường lập trình web.
Cấu trúc


Các thành phần
Model chứa dữ liệu và các tính toán xử lý logic để giải quyết vấn đề mà phần mềm hướng tới (business logic). Thành phần model thường được trình bày ở dạng Domain Model .
View là thành phần đảm nhận trình bày từ những dữ liệu của Model. View bao gồm những gì thể hiện trên màn hình như các control, form, widget,…
Controller là thành phần đảm nhận việc xử lý đáp trả lại các dữ liệu được đưa vào từ người dùng như các sự kiện chuột, bàn phím, các tương tác lên các control… Controller là cầu nối giữa người sử dụng và ứng dụng.
Phối hợp các thành phần
Trong kiến trúc MVC, mỗi bộ ba Model-View-Controller được thiết kế tương ứng cho các đối tượng mà người dùng có thể tương tác.
Model nắm giữ trạng thái, cấu trúc và các hành vi của dữ liệu được thể hiện và tương tác bởi người dùng. Model không phục thuộc và tương tác trực tiếp lên các thành phần khác. Thay vì vậy, khi có thay đổi, nó thông báo cho những thành phần như View tương ứng thông qua cơ chế là Observer pattern. Model còn cung cấp phương tiện để các thành phần khác tương tác lên nó.
View lấy các thông tin từ Model và trình bày đến người dùng. Trên cùng một model, có thể có nhiều View cùng đăng ký. Khi có một thay đổi từ Model, tất cả các View đều được thông báo thông qua observer mà nó đã đăng ký.
Mỗi View khi được tạo ra sẽ tạo ra một Controller đi kèm với nó. Trong khi các View đảm nhận kết xuất dữ liệu thì các Controller đảm nhận việc xử lý dữ liệu từ người dùng. Với mỗi sự kiện nhận được, Controller có thể xử lý và tương tác trực tiếp lên thành phần View và Model tương ứng để đáp trả. View và Controller là hai thành phần cấu thành nên giao diện người dùng của ứng dụng. Chúng lưu giữ liên kết trực tiếp đến Model. Trong khi Controller có thể thay đổi dữ liệu theo yêu cầu của người dùng thì View tương tác để lấy dữ liệu cập nhật vào chính nó từ Model.
Nhầm lẫn thường gặp
Một nhầm lẫn thường gặp trong quan hệ giữa các thành phần MVC là khi xem mục đích của Controller như là thành phần trung gian để tách rời View khỏi Model. Trong khi thực tế, kiến trúc MVC tách rời dữ liệu và xử lý trung tâm khỏi phần trình bày thông qua cơ chế là Observer Pattern chứ không phải Controller. Nhiệm vụ của Controller là cần nối giữa người dùng là ứng dụng, không phải giữa View và Model.
Vấn đề của MVC
Trong khi mục đích chính của MVC là tách rời trình bày và các xử lý bên trong. Việc phân rõ vai trò xử lý ouput (View) và input (Controller) là một hệ quả nhằm hoàn thiện cơ chế cho ý tưởng trên. Hiện nay trong nhiều môi trường lập trình hiện đại, nhiều control được cung cấp và hoàn thiện hơn (nhiều xử lý sự kiện cơ bản đã được hỗ trợ sẵn) so với trước đây nên việc khoáng tất cả các xử lý sự kiện cho một thành phần độc lập như Controller không còn là vấn đề quan trọng nữa.
Trong khi đó, những cơ chế như Observer Pattern cũng có vấn đề của riêng chúng. Trong khi được dùng như phương tiện hiệu quả để loại bỏ sự phụ thuộc của Model vào các thành phần khác, nó có một vấn đề lớn là tại một thời điểm, chúng ta khó có thể xác định điều gì sẽ xảy ra bằng cách đọc code và việc thực hiện các testing cũng khó khăn hơn. Hơn nữa, do Model chỉ liên kết gián tiếp đến View thông qua Observer Pattern, khi sự thay đổi trạng thái của Model cần đến một vài thao tác xử lý phức tạp để áp dụng lên giao diện thì với mô hình cổ điển sẽ gặp khó khăn.
Một vấn đề khác là chúng ta cần lưu trữ tình trạng hiện tại của UI (UI state), ví dụ trong danh sách sinh viên thì chúng ta cần biết sinh viên nào đang được chọn. Trong khi thành phần UI nắm giữ dữ liệu trình bày đang được chọn thì dữ liệu sinh viên thuộc về Model, như vậy dữ liệu về sinh viên được chọn sẽ được lưu trữ ở đâu khi cần truy xuất đến?
Vì những lí do trên, MVC sau này đã có những thay đổi và bổ sung nhất định (như khái niệm Application Model). Kiến trúc MVP chúng ta sẽ bàn dưới đây cũng dựa trên tư tưởng cơ bản của MVC nhưng với cách tiếp cận khác nhằm mục đích khắc phục các khuyết điểm đã có.