MVC (Model, View, Controller) là một mẫu để tổ chức mã trong một ứng dụng để cải thiện khả năng bảo trì.
Hãy tưởng tượng một nhiếp ảnh gia với máy ảnh của mình trong một studio. Một khách hàng yêu cầu anh ta chụp ảnh một chiếc hộp.
Các kiến trúc phi MVC có xu hướng được tích hợp chặt chẽ với nhau. Nếu hộp, bộ điều khiển và máy ảnh là một đối tượng giống nhau, thì chúng ta sẽ phải tách ra và sau đó xây dựng lại cả hộp và máy ảnh mỗi lần chúng ta muốn có một chế độ xem mới. Ngoài ra, chụp ảnh sẽ luôn giống như cố chụp ảnh tự sướng - và điều đó không phải lúc nào cũng dễ dàng.
bwaha đã viết:
Tác giả đề cập đến mvctree.py trong wxPython như một ví dụ về thiết kế MVC. Tuy nhiên tôi vẫn còn quá xanh nên tôi thấy ví dụ cụ thể đó quá phức tạp và tôi không hiểu sự tách biệt mà tác giả đang đề xuất.
MVC là tất cả về sự tách biệt của mối quan tâm.
Model chịu trách nhiệm quản lý dữ liệu của chương trình (cả dữ liệu riêng tư và dữ liệu khách hàng). Chế độ xem / Điều khiển chịu trách nhiệm cung cấp cho thế giới bên ngoài các phương tiện để tương tác với dữ liệu khách hàng của chương trình.
Mô hình cung cấp giao diện nội bộ (API) để cho phép các phần khác của chương trình tương tác với nó. Chế độ xem / Trình điều khiển cung cấp giao diện bên ngoài (GUI / CLI / biểu mẫu web / IPC cấp cao / v.v.) để cho phép mọi thứ nằm ngoài chương trình để giao tiếp với nó.
Model có trách nhiệm duy trì tính toàn vẹn của dữ liệu của chương trình, bởi vì nếu điều đó bị hỏng thì đó là trò chơi dành cho tất cả mọi người. Chế độ xem / Trình điều khiển chịu trách nhiệm duy trì tính toàn vẹn của giao diện người dùng, đảm bảo tất cả các chế độ xem văn bản đang hiển thị các giá trị cập nhật, vô hiệu hóa các mục menu không áp dụng cho tiêu điểm hiện tại, v.v.
Model không chứa mã View / Controller; không có các lớp widget GUI, không có mã để đặt hộp thoại hoặc nhận đầu vào của người dùng. Chế độ xem / Trình điều khiển không chứa mã Model; không có mã để xác thực URL hoặc thực hiện các truy vấn SQL và cũng không có trạng thái ban đầu: mọi dữ liệu được giữ bởi các widget chỉ nhằm mục đích hiển thị và chỉ đơn thuần là sự phản ánh của dữ liệu thực được lưu trữ trong Mô hình.
Bây giờ, đây là thử nghiệm của một thiết kế MVC thực sự: về bản chất chương trình phải có đầy đủ chức năng ngay cả khi không có Chế độ xem / Trình điều khiển đi kèm. OK, thế giới bên ngoài sẽ gặp khó khăn khi tương tác với nó ở dạng đó, nhưng miễn là người ta biết các câu thần chú API mô hình phù hợp, chương trình sẽ giữ và thao tác dữ liệu như bình thường.
Tại sao điều này có thể? Chà, câu trả lời đơn giản là tất cả là nhờ vào sự ghép nối thấp giữa các lớp Model và View / Controller. Tuy nhiên, đây không phải là câu chuyện đầy đủ. Chìa khóa của toàn bộ mô hình MVC là hướng mà các kết nối đó đi theo: TẤT CẢ các hướng dẫn từ Chế độ xem / Trình điều khiển đến Mô hình. Model KHÔNG BAO GIỜ cho View / Bộ điều khiển phải làm gì.
Tại sao? Bởi vì trong MVC, trong khi Chế độ xem / Trình điều khiển được phép biết một chút về Mô hình (cụ thể là API của Mô hình), nhưng Mô hình không được phép biết bất cứ điều gì về Chế độ xem / Trình điều khiển.
Tại sao? Bởi vì MVC là về việc tạo ra một sự tách biệt rõ ràng của mối quan tâm.
Tại sao? Để giúp ngăn chặn sự phức tạp của chương trình vượt khỏi tầm kiểm soát và chôn vùi bạn, nhà phát triển, theo nó. Chương trình càng lớn, số lượng thành phần trong chương trình đó càng lớn. Và càng có nhiều kết nối tồn tại giữa các thành phần đó, các nhà phát triển càng khó duy trì / mở rộng / thay thế các thành phần riêng lẻ hoặc thậm chí chỉ cần làm theo cách toàn bộ hệ thống hoạt động. Hãy tự hỏi mình điều này: khi nhìn vào sơ đồ cấu trúc của chương trình, bạn có muốn nhìn thấy một cái cây hay cái nôi của mèo không? Mẫu MVC tránh cái sau bằng cách không cho phép các kết nối vòng tròn: B có thể kết nối với A, nhưng A không thể kết nối với B. Trong trường hợp này, A là Model và B là View / Controller.
BTW, nếu bạn sắc sảo, bạn sẽ nhận thấy sự cố với hạn chế 'một chiều' vừa được mô tả: Làm thế nào Mô hình có thể thông báo cho Chế độ xem / Điều khiển các thay đổi trong dữ liệu người dùng của Mô hình khi Mô hình thậm chí không được phép biết rằng View / Controller, đừng bận tâm gửi tin nhắn đến nó? Nhưng đừng lo lắng: có một giải pháp cho vấn đề này, và nó khá gọn gàng ngay cả khi ban đầu nó có vẻ hơi vòng vo. Chúng tôi sẽ trở lại với điều đó trong một thời điểm.
Về mặt thực tế, sau đó, một đối tượng Chế độ xem / Trình điều khiển có thể, thông qua API của Mô hình, 1. yêu cầu Mô hình thực hiện (thực hiện các lệnh) và 2. yêu cầu Mô hình cung cấp cho nó mọi thứ (trả về dữ liệu). Lớp View / Controller
đẩy các hướng dẫn đến lớp Model và kéo thông tin từ lớp Model.
Và đó là nơi mà ví dụ MyCoolListControl đầu tiên của bạn gặp trục trặc, bởi vì API cho lớp đó yêu cầu thông tin đó được đẩy
vào đó, vì vậy bạn quay lại để có một khớp nối hai chiều giữa các lớp, vi phạm quy tắc MVC và đưa bạn trở lại vào kiến trúc cái nôi của mèo mà bạn [có lẽ] đang cố gắng tránh ở nơi đầu tiên.
Thay vào đó, lớp MyCoolListControl sẽ đi theo luồng, kéo dữ liệu cần thiết từ lớp bên dưới, khi cần. Trong trường hợp tiện ích danh sách, điều đó thường có nghĩa là hỏi có bao nhiêu giá trị và sau đó hỏi lần lượt từng mục đó, bởi vì đó là cách đơn giản nhất và lỏng lẻo nhất để thực hiện và do đó giữ cho khớp nối ở mức tối thiểu. Và nếu tiện ích muốn, giả sử, để trình bày các giá trị đó cho người dùng theo thứ tự bảng chữ cái đẹp thì đó là nhận thức của nó; và trách nhiệm của nó, tất nhiên.
Bây giờ, một câu hỏi hóc búa cuối cùng, như tôi đã gợi ý trước đó: làm thế nào để bạn giữ cho màn hình của UI được đồng bộ hóa với trạng thái của Model trong một hệ thống dựa trên MVC?
Đây là vấn đề: nhiều đối tượng Xem ở trạng thái, ví dụ: hộp kiểm có thể được đánh dấu hoặc chưa sử dụng, trường văn bản có thể chứa một số văn bản có thể chỉnh sửa. Tuy nhiên, MVC ra lệnh rằng tất cả dữ liệu người dùng được lưu trữ trong lớp Mô hình, do đó, bất kỳ dữ liệu nào được giữ bởi các lớp khác cho mục đích hiển thị (trạng thái của hộp kiểm, văn bản hiện tại của trường văn bản) phải là bản sao phụ của dữ liệu Mô hình chính đó. Nhưng nếu trạng thái của Mô hình thay đổi, bản sao trạng thái của Chế độ xem đó sẽ không còn chính xác nữa và cần được làm mới.
Nhưng bằng cách nào? Mẫu MVC ngăn Mô hình đẩy một bản sao mới của thông tin đó vào lớp View. Heck, nó thậm chí không cho phép Model gửi tin nhắn Xem thông báo trạng thái của nó đã thay đổi.
Vâng, gần như vậy. Được rồi, lớp Model không được phép nói chuyện trực tiếp với các lớp khác, vì để làm như vậy sẽ yêu cầu nó biết điều gì đó về các lớp đó và các quy tắc MVC ngăn chặn điều đó. Tuy nhiên, nếu một cái cây rơi trong rừng và không có ai xung quanh để nghe nó, liệu nó có phát ra âm thanh không?
Câu trả lời, bạn thấy, là thiết lập một hệ thống thông báo, cung cấp cho lớp Model một nơi mà nó có thể thông báo cho không ai nói riêng rằng nó vừa làm một điều gì đó thú vị. Các lớp khác sau đó có thể đăng người nghe với hệ thống thông báo đó để lắng nghe những thông báo mà họ thực sự quan tâm. Lớp Mô hình không cần biết gì về việc ai đang nghe (hoặc ngay cả khi có ai nghe cả!); nó chỉ đăng một thông báo và sau đó quên nó đi. Và nếu bất cứ ai nghe thấy thông báo đó và cảm thấy muốn làm gì đó sau đó - như hỏi Model cho một số dữ liệu mới để nó có thể cập nhật màn hình trên màn hình của nó - thì thật tuyệt. Mô hình chỉ liệt kê những thông báo mà nó gửi như một phần của định nghĩa API; và những gì người khác làm với kiến thức đó là tùy thuộc vào họ.
MVC được bảo tồn, và mọi người đều hạnh phúc. Khung ứng dụng của bạn cũng có thể cung cấp một hệ thống thông báo tích hợp hoặc bạn có thể tự viết nếu không (xem 'mẫu quan sát viên').
...
Dù sao, hy vọng rằng sẽ giúp. Khi bạn hiểu được các động lực đằng sau MVC, lý do tại sao mọi thứ được thực hiện theo cách chúng bắt đầu có ý nghĩa, ngay cả khi - thoạt nhìn - chúng có vẻ phức tạp hơn mức cần thiết.
Chúc mừng
có