MVVM và mẫu dịch vụ


13

Tôi đang xây dựng một ứng dụng WPF bằng cách sử dụng mẫu MVVM. Ngay bây giờ, viewmodels của tôi gọi lớp dịch vụ để lấy các mô hình (làm thế nào không liên quan đến viewmodel) và chuyển đổi chúng thành viewmodels. Tôi đang sử dụng phương thức tiêm constructor để truyền dịch vụ cần thiết cho viewmodel.

Nó dễ dàng kiểm tra và hoạt động tốt đối với các chế độ xem với một vài phụ thuộc, nhưng ngay khi tôi cố gắng tạo viewModels cho các mô hình phức tạp, tôi có một hàm tạo với rất nhiều dịch vụ được đưa vào (một để lấy từng phụ thuộc và một danh sách tất cả các giá trị có sẵn để liên kết với một mụcSource chẳng hạn). Tôi đang tự hỏi làm thế nào để xử lý nhiều dịch vụ như vậy mà vẫn có chế độ xem mà tôi có thể kiểm tra đơn vị một cách dễ dàng.

Tôi đang nghĩ về một vài giải pháp:

  1. Tạo một dịch vụ đơn (IService) chứa tất cả các dịch vụ có sẵn dưới dạng giao diện. Ví dụ: Services.C Hiện tại.XXXService.Retrieve (), Services.C Hiện.YYYService.Retrieve (). Theo cách đó, tôi không có một nhà xây dựng lớn với hàng tấn thông số dịch vụ trong đó.

  2. Tạo mặt tiền cho các dịch vụ được sử dụng bởi viewModel và truyền đối tượng này vào ctor của viewmodel của tôi. Nhưng sau đó, tôi sẽ phải tạo một mặt tiền cho mỗi chế độ xem phức tạp của mình và có thể hơi ...

Bạn nghĩ gì là cách "đúng" để thực hiện loại kiến ​​trúc này?


Tôi nghĩ rằng cách "đúng" để làm điều đó là tạo một lớp riêng gọi các dịch vụ và thực hiện bất kỳ việc truyền nào là cần thiết để tạo ViewModel. ViewModels của bạn không chịu trách nhiệm tự tạo.
Amy Blankenship

@AmyBlankenship: Các mô hình xem không cần phải (hoặc thậm chí nhất thiết có thể) tự tạo, nhưng đôi khi chắc chắn sẽ chịu trách nhiệm tạo các mô hình xem khác . Một container IoC với sự hỗ trợ của nhà máy tự động là một sự trợ giúp rất lớn ở đây.
Aaronaught

"Đôi khi sẽ" và "nên" là hai động vật khác nhau;)
Amy Blankenship

@AmyBlankenship: Bạn có gợi ý rằng các mô hình xem không nên tạo các mô hình xem khác không? Đó là một viên thuốc khó nuốt. Tôi có thể hiểu rằng nói rằng các mô hình xem không nên sử dụng newđể tạo các mô hình xem khác, nhưng hãy nghĩ về một thứ đơn giản như ứng dụng MDI khi nhấp vào nút hoặc menu "tài liệu mới" sẽ thêm một tab mới hoặc mở một cửa sổ mới. Vỏ / dây dẫn phải có khả năng tạo ra các phiên bản mới của một thứ gì đó , ngay cả khi nó bị ẩn đằng sau một hoặc một vài lớp cảm ứng.
Aaronaught

Chà, chắc chắn nó phải có khả năng yêu cầu Chế độ xem được thực hiện ở đâu đó. Nhưng để làm cho chính nó? Không phải trong thế giới của tôi :). Nhưng một lần nữa, ở thế giới tôi sống, chúng tôi gọi VM là "Mô hình trình bày".
Amy Blankenship

Câu trả lời:


22

Trong thực tế, cả hai giải pháp này đều xấu.

Tạo một dịch vụ đơn (IService) chứa tất cả các dịch vụ có sẵn dưới dạng giao diện. Ví dụ: Services.C Hiện tại.XXXService.Retrieve (), Services.C Hiện.YYYService.Retrieve (). Theo cách đó, tôi không có một nhà xây dựng lớn với hàng tấn thông số dịch vụ trong đó.

Đây thực chất là Mẫu định vị dịch vụ , là mẫu chống. Nếu bạn làm điều này, bạn sẽ không còn có thể hiểu mô hình khung nhìn thực sự phụ thuộc vào cái gì mà không nhìn vào triển khai riêng tư của nó, điều này sẽ gây khó khăn cho việc kiểm tra hoặc cấu trúc lại.

Tạo mặt tiền cho các dịch vụ được sử dụng bởi viewModel và truyền đối tượng này vào ctor của viewmodel của tôi. Nhưng sau đó, tôi sẽ phải tạo một mặt tiền cho mỗi chế độ xem phức tạp của mình và có thể hơi ...

Đây không phải là quá nhiều chống mẫu nhưng nó là một mùi mã. Về cơ bản, bạn đang tạo một đối tượng tham số , nhưng điểm của mẫu tái cấu trúc PO là xử lý các bộ tham số được sử dụng thường xuyên và ở nhiều nơi khác nhau , trong khi tham số này chỉ được sử dụng một lần. Như bạn đã đề cập, nó sẽ tạo ra rất nhiều sự phình to mã mà không có lợi ích thực sự, và sẽ không chơi tốt với nhiều container IoC.

Trên thực tế, cả hai chiến lược trên đều xem xét vấn đề tổng thể, đó là sự khớp nối quá cao giữa các mô hình và dịch vụ xem . Chỉ đơn giản là ẩn các phụ thuộc này trong một bộ định vị dịch vụ hoặc đối tượng tham số không thực sự thay đổi số lượng đối tượng khác mà mô hình khung nhìn phụ thuộc vào.

Hãy nghĩ về cách bạn sẽ kiểm tra đơn vị một trong những mô hình xem này. Mã thiết lập của bạn sẽ lớn đến mức nào? Có bao nhiêu thứ cần được khởi tạo để nó hoạt động?

Rất nhiều người bắt đầu với MVVM cố gắng tạo các mô hình xem cho toàn bộ màn hình , về cơ bản là cách tiếp cận sai. MVVM là tất cả về thành phần và một màn hình có nhiều chức năng nên bao gồm một số mô hình khung nhìn khác nhau, mỗi mô hình chỉ phụ thuộc vào một hoặc một vài mô hình / dịch vụ nội bộ. Nếu họ cần liên lạc với nhau, bạn thực hiện thông qua pub / sub (người môi giới tin nhắn, xe buýt sự kiện, v.v.)

Những gì bạn thực sự cần làm là cấu trúc lại các mô hình xem của bạn để chúng có ít phụ thuộc hơn . Sau đó, nếu bạn cần có một "màn hình" tổng hợp, bạn tạo một mô hình khung nhìn khác để tổng hợp các mô hình khung nhìn nhỏ hơn. Mô hình khung nhìn tổng hợp này không phải tự làm rất nhiều, do đó, nó cũng khá dễ hiểu và dễ kiểm tra.

Nếu bạn đã thực hiện điều này đúng cách, thì rõ ràng chỉ cần nhìn vào mã, bởi vì bạn sẽ có các mô hình xem ngắn, ngắn gọn, cụ thể và có thể kiểm tra được.


Vâng, đó là những gì tôi có thể sẽ làm! Cảm ơn ngài rất nhiều.
alfa-alfa

Chà, tôi đã cho rằng anh ta đã thử nó, nhưng không thành công. @ alfa-alfa
Euphoric

@Euphoric: Làm thế nào để bạn "không thành công" trong việc này? Như Yoda sẽ nói: Làm hay không làm, không có thử.
Aaronaught

@Aaronaught Ví dụ anh ấy thực sự cần tất cả dữ liệu trong một viewmodel duy nhất. Có thể anh ta có lưới và các cột khác nhau đến từ các dịch vụ khác nhau. Bạn không thể làm điều đó với thành phần.
Euphoric

@Euphoric: Trên thực tế, bạn có thể giải quyết điều đó bằng bố cục , nhưng điều đó có thể được thực hiện dưới mức mô hình xem. Nó đơn giản chỉ là vấn đề tạo ra sự trừu tượng đúng đắn. Trong trường hợp đó, bạn chỉ cần một dịch vụ để xử lý truy vấn ban đầu để có được danh sách ID và một chuỗi / danh sách / mảng "người làm giàu" chú thích với thông tin của riêng họ. Biến lưới thành mô hình khung nhìn của riêng nó và bạn đã giải quyết vấn đề bằng hai cách phụ thuộc một cách hiệu quả và cực kỳ dễ kiểm tra.
Aaronaught

1

Tôi có thể viết một cuốn sách về điều này ... thực tế là tôi;)

Trước hết, không có phổ biến cách "đúng" để làm việc. Bạn phải xem xét các yếu tố khác.

Có thể các dịch vụ của bạn quá tốt. Kết hợp các Dịch vụ với Mặt tiền cung cấp giao diện mà một Viewmodel cụ thể hoặc thậm chí một cụm ViewModels có liên quan sẽ sử dụng có thể là một giải pháp tốt hơn.

Đơn giản hơn vẫn là bọc các dịch vụ thành một Mặt tiền duy nhất mà tất cả các chế độ xem sử dụng. Tất nhiên đó có khả năng có thể là một giao diện rất lớn với rất nhiều chức năng không cần thiết cho kịch bản trung bình. Nhưng tôi sẽ nói nó không khác gì một bộ định tuyến tin nhắn xử lý mọi tin nhắn trong hệ thống của bạn.

Trên thực tế, những gì tôi đã thấy rất nhiều kiến ​​trúc cuối cùng phát triển thành một chiếc xe buýt tin nhắn được xây dựng xung quanh một cái gì đó giống như mẫu Tổng hợp sự kiện. Việc kiểm tra rất dễ dàng vì lớp kiểm tra của bạn chỉ cần đăng ký một người nghe với EA và thực hiện phản ứng thích hợp. Nhưng đó là một kịch bản trước cần có thời gian để phát triển. Tôi nói bắt đầu với mặt tiền thống nhất và đi từ đó.


Một mặt tiền dịch vụ lớn rất khác với một nhà môi giới tin nhắn. Nó gần như ở phía đối diện của phổ phụ thuộc. Một đặc điểm nổi bật của kiến ​​trúc này là người gửi không biết gì về người nhận và có thể có nhiều người nhận (pub / sub hoặc multicast). Có lẽ bạn đang nhầm lẫn nó với "từ xa" kiểu RPC, chỉ hiển thị một dịch vụ truyền thống qua giao thức từ xa, và người gửi vẫn được kết hợp với nhận, cả về mặt vật lý (địa chỉ điểm cuối) và logic (giá trị trả về).
Aaronaught

Điểm giống nhau là mặt tiền hoạt động như một bộ định tuyến, người gọi không biết dịch vụ / dịch vụ nào xử lý cuộc gọi giống như khách hàng gửi tin nhắn không biết ai xử lý tin nhắn.
Michael Brown

Vâng, nhưng mặt tiền sau đó là một đối tượng Thiên Chúa . Nó có tất cả các phụ thuộc mà Mô hình xem có, có thể nhiều hơn bởi vì nó sẽ được chia sẻ bởi một số người. Thực tế, bạn đã loại bỏ những lợi ích của khớp nối lỏng lẻo mà bạn đã làm việc rất chăm chỉ ngay từ đầu, bởi vì bây giờ, bất cứ khi nào có thứ gì đó chạm vào mặt tiền lớn, bạn không biết nó thực sự phụ thuộc vào chức năng nào. Hình ảnh viết một bài kiểm tra đơn vị cho một lớp sử dụng mặt tiền. Bạn tạo một mock cho mặt tiền. Bây giờ, những phương pháp nào bạn chế nhạo? Mã thiết lập của bạn trông như thế nào?
Aaronaught

Điều này rất khác với một nhà môi giới tin nhắn vì nhà môi giới cũng không biết gì về việc thực hiện các trình xử lý tin nhắn . Nó sử dụng IoC dưới mui xe. Mặt tiền biết mọi thứ về người nhận bởi vì nó phải chuyển tiếp các cuộc gọi đến họ. Xe buýt có khớp nối bằng không; mặt tiền có khớp nối tràn đầy cao. Hầu như mọi thứ bạn thay đổi, ở bất cứ đâu, cũng sẽ ảnh hưởng đến mặt tiền.
Aaronaught

Tôi nghĩ rằng một phần của sự nhầm lẫn ở đây - và tôi thấy điều này khá nhiều - chỉ là ý nghĩa của sự phụ thuộc . Nếu bạn có một lớp phụ thuộc vào một lớp khác, nhưng gọi 4 phương thức của lớp đó, thì nó có 4 phụ thuộc chứ không phải 1. Đặt tất cả phía sau một mặt tiền không làm thay đổi số lượng phụ thuộc, điều đó chỉ khiến chúng khó hiểu hơn .
Aaronaught

0

Tại sao không kết hợp cả hai?

Tạo mặt tiền và đặt tất cả các dịch vụ mà chế độ xem của bạn sử dụng. Sau đó, bạn có thể có mặt tiền duy nhất cho tất cả yourviewmodels mà không có từ S xấu.

Hoặc bạn có thể sử dụng thuộc tính tiêm thay vì tiêm xây dựng. Nhưng sau đó, bạn cần đảm bảo rằng những người được tiêm đúng cách.


Đây sẽ là một câu trả lời tốt hơn nếu bạn cung cấp một ví dụ trong pseudo-C #.
Robert Harvey
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.