Làm cách nào để tạo chế độ xem trong thời gian chạy ít đau hơn


17

Tôi xin lỗi vì câu hỏi dài, nó đọc hơi khó hiểu, nhưng tôi hứa là không! Tôi đã tóm tắt (các) câu hỏi của tôi dưới đây

Trong thế giới MVC, mọi thứ rất đơn giản. Mô hình có trạng thái, Chế độ xem hiển thị Mô hình và Bộ điều khiển thực hiện / với Mô hình (về cơ bản), bộ điều khiển không có trạng thái. Để làm công cụ Bộ điều khiển có một số phụ thuộc vào dịch vụ web, kho lưu trữ, rất nhiều. Khi bạn khởi tạo một bộ điều khiển bạn quan tâm đến việc cung cấp các phụ thuộc đó, không có gì khác. Khi bạn thực thi một hành động (phương thức trên Bộ điều khiển), bạn sử dụng các phụ thuộc đó để truy xuất hoặc cập nhật Mô hình hoặc gọi một số dịch vụ miền khác. Nếu có bất kỳ bối cảnh nào, giả sử như một số người dùng muốn xem chi tiết của một mặt hàng cụ thể, bạn chuyển Id của mặt hàng đó làm tham số cho Hành động. Không nơi nào trong Bộ điều khiển có bất kỳ tham chiếu nào đến bất kỳ trạng thái nào. Càng xa càng tốt.

Nhập MVVM. Tôi yêu WPF, tôi thích ràng buộc dữ liệu. Tôi yêu các khung làm cho việc liên kết dữ liệu với ViewModels trở nên dễ dàng hơn (sử dụng Caliburn Micro atm). Tôi cảm thấy mọi thứ ít đơn giản hơn trong thế giới này mặc dù. Chúng ta hãy làm việc thực hiện một lần nữa: Model có nhà nước, Xem show ViewModel, và ViewModel làm công cụ để / với Model (cơ bản), một ViewModel không có nhà nước! (để làm rõ, có thể nó đại biểu tất cả các thuộc tính cho một hoặc nhiều mô hình, nhưng điều đó có nghĩa là nó phải có một tham chiếu đến các mô hình một cách này hay cách khác, đó là tình trạng của riêng mình) Để làmThứ ViewModel có một số phụ thuộc vào dịch vụ web, kho lưu trữ, rất nhiều. Khi bạn khởi tạo ViewModel, bạn quan tâm đến việc cung cấp các phụ thuộc đó, mà cả trạng thái. Và điều này, thưa quý vị và các bạn, làm phiền tôi đến tận cùng.

Bất cứ khi nào bạn cần khởi tạo một ProductDetailsViewModeltừ ProductSearchViewModel(từ đó bạn gọi là ProductSearchWebServicelần lượt trả lại IEnumerable<ProductDTO>, mọi người vẫn ở với tôi?), Bạn có thể thực hiện một trong những điều sau:

  • Gọi new ProductDetailsViewModel(productDTO, _shoppingCartWebService /* dependcy */);, điều này là xấu, hãy tưởng tượng thêm 3 phụ thuộc, điều này có nghĩa là cũng ProductSearchViewModelcần phải có những phụ thuộc đó. Cũng thay đổi các nhà xây dựng là đau đớn.
  • Gọi _myInjectedProductDetailsViewModelFactory.Create().Initialize(productDTO);, nhà máy chỉ là một Func, chúng dễ dàng được tạo bởi hầu hết các khung IoC. Tôi nghĩ rằng điều này là xấu bởi vì các phương thức init là một sự trừu tượng bị rò rỉ. Bạn cũng không thể sử dụng từ khóa chỉ đọc cho các trường được đặt trong phương thức init. Tôi chắc chắn có một vài lý do nữa.
  • gọi _myInjectedProductDetailsViewModelAbstractFactory.Create(productDTO);Vì vậy ... đây là mô hình (nhà máy trừu tượng) thường được đề xuất cho loại vấn đề này. Tôi mặc dù nó là thiên tài vì nó thỏa mãn sự khao khát của tôi đối với việc gõ tĩnh, cho đến khi tôi thực sự bắt đầu sử dụng nó. Số lượng mã soạn sẵn là tôi nghĩ quá nhiều (bạn biết, ngoài các tên biến vô lý tôi sử dụng). Đối với mỗi ViewModel cần tham số thời gian chạy, bạn sẽ nhận được hai tệp bổ sung (giao diện xuất xưởng và triển khai) và bạn cần nhập các phụ thuộc không phải thời gian chạy như 4 lần thêm. Và mỗi lần thay đổi phụ thuộc, bạn cũng có thể thay đổi nó trong nhà máy. Có cảm giác như tôi thậm chí không sử dụng container DI nữa. (Tôi nghĩ Castle Windsor có một số giải pháp cho việc này [với những nhược điểm riêng, hãy sửa tôi nếu tôi sai]).
  • làm một cái gì đó với các loại ẩn danh hoặc từ điển. Tôi thích gõ tĩnh của tôi.

Vì vậy, vâng. Trộn lẫn trạng thái và hành vi theo cách này tạo ra một vấn đề hoàn toàn không tồn tại trong MVC. Và tôi cảm thấy như hiện tại không có một giải pháp thực sự thích hợp cho vấn đề này. Bây giờ tôi muốn quan sát một số điều:

  • Mọi người thực sự sử dụng MVVM. Vì vậy, họ hoặc không quan tâm đến tất cả những điều trên, hoặc họ có một số giải pháp tuyệt vời khác.
  • Tôi chưa tìm thấy một ví dụ chuyên sâu về MVVM với WPF. Ví dụ, dự án mẫu NDD đã giúp tôi hiểu một số khái niệm DDD. Tôi thực sự thích nó nếu ai đó có thể chỉ cho tôi hướng đi của một thứ tương tự cho MVVM / WPF.
  • Có lẽ tôi đang làm sai MVVM và tôi nên đảo ngược thiết kế của mình. Có lẽ tôi không nên có vấn đề này cả. Tôi biết những người khác đã hỏi cùng một câu hỏi vì vậy tôi nghĩ tôi không phải là người duy nhất.

Tóm tắt

  • Tôi có đúng không khi kết luận rằng việc ViewModel là một điểm tích hợp cho cả trạng thái và hành vi là lý do cho một số khó khăn với toàn bộ mẫu MVVM?
  • Có phải việc sử dụng mẫu nhà máy trừu tượng là cách duy nhất / tốt nhất để khởi tạo ViewModel theo cách gõ tĩnh?
  • Có một cái gì đó giống như một triển khai tham khảo chuyên sâu có sẵn?
  • Có nhiều ViewModels với cả trạng thái / hành vi là mùi thiết kế không?

10
Điều này quá dài để đọc, xem xét sửa đổi, có rất nhiều thứ không liên quan trong đó. Bạn có thể bỏ lỡ câu trả lời tốt vì mọi người sẽ không bận tâm để đọc tất cả.
yannis

Bạn nói rằng bạn yêu Caliburn.Micro, nhưng bạn không biết làm thế nào khung này có thể giúp khởi tạo các mô hình xem mới? Kiểm tra một số ví dụ về nó.
Euphoric

@Euphoric Bạn có thể cụ thể hơn một chút không, Google dường như không giúp tôi ở đây. Có một số từ khóa tôi có thể tìm kiếm?
dvvorle

3
Tôi nghĩ rằng bạn đang đơn giản hóa MVC một chút. Chắc chắn Chế độ xem hiển thị Mô hình lúc đầu, nhưng trong khi vận hành, trạng thái thay đổi. Theo tôi, trạng thái thay đổi này là "Mô hình chỉnh sửa". Đó là, một phiên bản phẳng của Mô hình với các hạn chế về tính nhất quán giảm. Trong thực tế, cái mà tôi gọi là Mô hình chỉnh sửa là MVVM ViewModel. Nó giữ trạng thái trong khi đang chuyển đổi, trước đây được giữ bởi Chế độ xem trong MVC hoặc bị đẩy lùi vào phiên bản Mô hình không được cam kết, nơi tôi không nghĩ nó thuộc về. Vì vậy, bạn đã có trạng thái "trong thông lượng" trước đó. Bây giờ tất cả đã có trong ViewModel.
Scott Whitlock

@ScottWhitlock Tôi thực sự đơn giản hóa MVC. Nhưng tôi không nói sai rằng trạng thái "thông lượng" nằm trong ViewModel, tôi đang nói rằng việc nhồi nhét hành vi trong đó cũng khiến cho việc khởi tạo ViewModel thành trạng thái có thể sử dụng được từ một ViewModel khác trở nên khó khăn hơn. "Mô hình chỉnh sửa" của bạn trong MVC không biết cách tự lưu (nó không có phương thức Lưu). Nhưng bộ điều khiển biết điều này và có tất cả các phụ thuộc cần thiết để làm điều đó.
dvvorle

Câu trả lời:


2

Vấn đề phụ thuộc khi bắt đầu một mô hình khung nhìn mới có thể được xử lý bằng IOC.

public class MyCustomViewModel{
  private readonly IShoppingCartWebService _cartService;

  private readonly ITimeService _timeService;

  public ProductDTO ProductDTO { get; set; }

  public ProductDetailsViewModel(IShoppingCartWebService cartService, ITimeService timeService){
    _cartService = cartService;
    _timeService = timeService;
  }
}

Khi thiết lập container ...

Container.Register<IShoppingCartWebService,ShoppingCartWebSerivce>().As.Singleton();
Container.Register<ITimeService,TimeService>().As.Singleton();
Container.Register<ProductDetailsViewModel>();

Khi bạn cần mô hình xem của bạn:

var viewmodel = Container.Resolve<ProductDetailsViewModel>();
viewmodel.ProductDTO = myProductDTO;

Khi sử dụng một khung như micro caliburn , thường có một số dạng thùng chứa IOC.

SomeCompositionView view = new SomeCompositionView();
ISomeCompositionViewModel viewModel = IoC.Get<ISomeCompositionViewModel>();
ViewModelBinder.Bind(viewModel, view, null);

1

Tôi làm việc hàng ngày với ASP.NET MVC và đã làm việc trên WPF được hơn một năm và đây là cách tôi thấy nó:

MVC

Bộ điều khiển có nhiệm vụ phối hợp các hành động (lấy cái này, thêm cái kia).

Khung nhìn có trách nhiệm hiển thị mô hình.

Mô hình thường bao gồm dữ liệu (ví dụ: UserId, FirstName) cũng như trạng thái (ví dụ: Tiêu đề) và thường được xem cụ thể.

MVVM

Mô hình thường chỉ chứa dữ liệu (ví dụ: UserId, FirstName) và thường được truyền xung quanh

Mô hình khung nhìn bao gồm hành vi của khung nhìn (phương thức), dữ liệu (mô hình) và tương tác (lệnh) - tương tự như mẫu MVP đang hoạt động nơi người trình bày biết về mô hình. Mô hình khung nhìn là khung nhìn cụ thể (1 view = 1 view model).

Khung nhìn chịu trách nhiệm hiển thị dữ liệu và liên kết dữ liệu với mô hình khung nhìn. Khi một khung nhìn được tạo, thông thường mô hình khung nhìn liên kết của nó được tạo với nó.


Điều bạn nên nhớ là mẫu trình bày MVVM dành riêng cho WPF / Silverlight do tính chất ràng buộc dữ liệu của chúng.

Khung nhìn thường biết mô hình khung nhìn nào được liên kết với (hoặc sự trừu tượng hóa của một khung nhìn).

Tôi sẽ khuyên bạn nên coi mô hình khung nhìn là một singleton, mặc dù nó được khởi tạo trên mỗi lượt xem. Nói cách khác, bạn sẽ có thể tạo nó thông qua DI thông qua bộ chứa IOC và gọi các phương thức thích hợp để nói; tải mô hình của nó dựa trên các tham số. Một cái gì đó như thế này:

public partial class EditUserView
{
    public EditUserView(IContainer container, int userId) : this() {
        var viewModel = container.Resolve<EditUserViewModel>();
        viewModel.LoadModel(userId);
        DataContext = viewModel;
    }
}

Ví dụ trong trường hợp này, bạn sẽ không tạo mô hình chế độ xem cụ thể cho người dùng đang được cập nhật - thay vào đó mô hình sẽ chứa dữ liệu cụ thể của người dùng được tải qua một số cuộc gọi trên mô hình chế độ xem.


Nếu FirstName của tôi là "Peter" và Tiêu đề của tôi là {"Rev", "Dr"} *, tại sao bạn lại xem xét dữ liệu FirstName và trạng thái Tiêu đề? Hoặc bạn có thể làm rõ ví dụ của bạn? * không thực sự
Pete Kirkham

@PeteKirkham - ví dụ về 'tiêu đề' mà ​​tôi đã đề cập đến trong bối cảnh nói một combobox. Nói chung, khi bạn gửi thông tin để được lưu giữ, bạn sẽ không gửi trạng thái (ví dụ: danh sách các tiểu bang / tỉnh / tiêu đề) đã được sử dụng để thực hiện lựa chọn. Bất kỳ trạng thái đáng giá nào để chuyển với dữ liệu (ví dụ: tên người dùng đang sử dụng) nên được kiểm tra tại điểm xử lý vì trạng thái có thể đã trở nên cũ (nếu bạn đang sử dụng một số mẫu không đồng bộ như xếp hàng tin nhắn).
Shelakel

Mặc dù đã hai năm kể từ bài đăng này, tôi phải đưa ra nhận xét có lợi cho người xem trong tương lai: Hai điều làm tôi băn khoăn với câu trả lời của bạn. Chế độ xem có thể tương ứng với một ViewModel, nhưng ViewModel có thể được đại diện bởi một số Chế độ xem. Thứ hai, những gì bạn đang mô tả là mô hình chống định vị dịch vụ. IMHO bạn không nên trực tiếp giải quyết các chế độ xem ở mọi nơi. Đó là những gì DI dành cho. Làm cho quyết tâm của bạn ở ít điểm nhất có thể. Hãy để Caliburn làm việc này cho bạn, ví dụ.
Jony Adamit 19/03/2015

1

Câu trả lời ngắn cho câu hỏi của bạn:

  1. Có Trạng thái + Hành vi dẫn đến những vấn đề đó, nhưng điều này đúng với tất cả các OO. Thủ phạm thực sự là sự kết hợp của ViewModels, một loại vi phạm SRP.
  2. Gõ tĩnh, có lẽ. Nhưng bạn nên giảm / loại bỏ nhu cầu khởi tạo ViewModels từ các ViewModels khác.
  3. Không phải là tôi biết.
  4. Không, nhưng có ViewModels với trạng thái & hành vi không liên quan (Giống như một số tham chiếu Mô hình và một số tham chiếu ViewModel)

Phiên bản dài:

Chúng tôi đang đối mặt với cùng một vấn đề và tìm thấy một số điều có thể giúp bạn. Mặc dù tôi không biết giải pháp "ma thuật", nhưng những điều đó đang làm dịu cơn đau một chút.

  1. Triển khai các mô hình liên kết từ DTOs để theo dõi thay đổi và xác nhận. Những "Dữ liệu" -ViewModels đó không được phụ thuộc vào các dịch vụ và không đến từ vùng chứa. Họ có thể chỉ là "người mới", được thông qua và thậm chí có thể xuất phát từ DTO. Điểm mấu chốt là triển khai một Model cụ thể cho ứng dụng của bạn (Giống như MVC).

  2. Tách rời ViewModels của bạn. Caliburn giúp dễ dàng ghép đôi ViewModels với nhau. Nó thậm chí còn gợi ý nó thông qua mô hình Screen / Conductor của nó. Nhưng sự kết hợp này làm cho ViewModels khó kiểm tra đơn vị, tạo ra nhiều phụ thuộc và quan trọng nhất: Áp đặt gánh nặng quản lý vòng đời ViewModel lên ViewModels của bạn. Một cách để tách chúng là sử dụng một cái gì đó như dịch vụ điều hướng hoặc bộ điều khiển ViewModel. Ví dụ

    giao diện công cộng IShowViewModels {void Show (object inlineArgumentAsAnonymousType, chuỗi areaId); }

Thậm chí tốt hơn là làm điều này bằng một số hình thức nhắn tin. Nhưng điều quan trọng là không xử lý vòng đời ViewModel từ các ViewModels khác. Trong Bộ điều khiển MVC không phụ thuộc vào nhau và trong MVVM ViewModels không nên phụ thuộc vào nhau. Tích hợp chúng thông qua một số cách khác.

  1. Sử dụng các tính năng động "theo kiểu nghiêm ngặt" của bộ chứa. Mặc dù có thể tạo ra một cái gì đó giống như INeedData<T1,T2,...>và thực thi các tham số tạo an toàn loại, nhưng nó không có giá trị. Ngoài ra việc tạo các nhà máy cho từng loại ViewModel là không xứng đáng. Hầu hết các Container IoC cung cấp giải pháp cho việc này. Bạn sẽ gặp lỗi khi chạy nhưng khả năng khử ghép và kiểm tra đơn vị là xứng đáng. Bạn vẫn thực hiện một số loại kiểm tra tích hợp và những lỗi đó được phát hiện dễ dàng.

0

Cách tôi thường làm điều này (sử dụng PRISM), là mỗi cụm chứa một mô-đun khởi tạo vùng chứa, trong đó tất cả các giao diện, phiên bản được đăng ký khi khởi động.

private void RegisterResources()
{
    Container.RegisterType<IDataService, DataService>();
    Container.RegisterType<IProductSearchViewModel, ProductSearchViewModel>();
    Container.RegisterType<IProductDetailsViewModel, ProductDetailsViewModel>();
}

Và đưa ra các lớp ví dụ của bạn, sẽ được thực hiện như thế này, với container được chuyển qua tất cả các cách. Bằng cách này, bất kỳ phụ thuộc mới nào cũng có thể được thêm dễ dàng vì bạn đã có quyền truy cập vào container.

/// <summary>
/// IDataService Interface
/// </summary>
public interface IDataService
{
    DataTable GetSomeData();
}

public class DataService : IDataService
{
    public DataTable GetSomeData()
    {
        MessageBox.Show("This is a call to the GetSomeData() method.");

        var someData = new DataTable("SomeData");
        return someData;
    }
}

public interface IProductSearchViewModel
{
}

public class ProductSearchViewModel : IProductSearchViewModel
{
    private readonly IUnityContainer _container;

    /// <summary>
    /// This will get resolved if it's been added to the container.
    /// Or alternately you could use constructor resolution. 
    /// </summary>
    [Dependency]
    public IDataService DataService { get; set; }

    public ProductSearchViewModel(IUnityContainer container)
    {
        _container = container;
    }

    public void SearchAndDisplay()
    {
        DataTable results = DataService.GetSomeData();

        var detailsViewModel = _container.Resolve<IProductDetailsViewModel>();
        detailsViewModel.DisplaySomeDataInView(results);

        // Create the view, usually resolve using region manager etc.
        var detailsView = new DetailsView() { DataContext = detailsViewModel };
    }
}

public interface IProductDetailsViewModel
{
    void DisplaySomeDataInView(DataTable dataTable);
}

public class ProductDetailsViewModel : IProductDetailsViewModel
{
    private readonly IUnityContainer _container;

    public ProductDetailsViewModel(IUnityContainer container)
    {
        _container = container;
    }

    public void DisplaySomeDataInView(DataTable dataTable)
    {
    }
}

Điều khá phổ biến là có một lớp ViewModelBase, mà tất cả các mô hình khung nhìn của bạn đều được lấy từ đó, có chứa một tham chiếu đến vùng chứa. Miễn là bạn có thói quen giải quyết tất cả các mô hình xem thay vì new()'ingchúng, điều đó sẽ làm cho mọi giải pháp phụ thuộc đơn giản hơn rất nhiều.


0

Đôi khi, thật tốt khi đi đến định nghĩa đơn giản hơn là một ví dụ đầy đủ: http://en.wikipedia.org/wiki/Model_View_ViewModel có thể đọc ví dụ Java ZK sáng hơn so với C #.

Những lần khác hãy lắng nghe bản năng ruột thịt của bạn ...

Có nhiều ViewModels với cả trạng thái / hành vi là mùi thiết kế không?

Các mô hình của bạn là đối tượng trên mỗi ánh xạ bảng? Có lẽ một ORM sẽ giúp ánh xạ tới các đối tượng miền trong khi xử lý doanh nghiệp hoặc cập nhật nhiều bảng.

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.