2009-06-08 4 views
38

Кажется, что ViewModels, что я выгляжу как подозрительно, как и другие классы, и им кажется, что требуется много повторений кода, например. в текущем проекте у меня есть:Нарушает ли MVVM DRY?

  • SmartForm: Модель, что представляет собой форму данных для заполнения, имеет свойства:
    • IDCODE
    • Название
    • Описание
    • коллекция от SmartFormFields
    • и др.
  • SmartFormControlViewПосмотреть
  • SmartFormControlViewModelViewModel
    • IDCODE
    • Название
    • Описание
    • коллекция SmartFormFields
    • т.д.

Так что мой ViewModel в основном такая же, как моя модель, только со всеми OnPropertyChanged функциями для связывания с видом.

Кажется, я реорганизовать и расширить это, что каждые мало изменений я делаю для моей модели, я должны внести изменения взеркала в ViewModel.

Это, кажется, нарушает основное правило шаблонов Не повторяйте себя.

Я внедряю шаблон MVVM неправильно или это просто неотъемлемая характеристика MVVM, что между моделью и ViewModel всегда происходит повторение 1 к 1?

+1

Я часто задавался вопросом то же самое ... кроме как в моем случае, я также решил реализовать уведомления об изменениях в своих моделях (имеет больше смысла для меня - они здесь, где данные сохраняются, поэтому они будут знать, когда это изменится). Это делает повторение выделяющимся еще больше. –

+1

Мой подход заключается в том, что модели должны быть доступны в любой среде, поэтому, если вы хотите использовать их в приложении ASP.NET MVC, вы не хотите получать уведомления об изменениях. Поэтому я думаю, что уведомления об изменениях на ваших моделях делают их заблокированными в среде WPF, верно? –

+1

Почему бы просто не иметь экземпляр класса SmartForm в SmartFormControlViewModel вместо репликации полей? –

ответ

21

Я лично не считаю, что это нарушает СУХОЙ, так как модель и модель представления (я предпочитаю термин ведущий) не указывают на ту же информацию. Например, ваши виртуальные машины и M имеют свойство Title, но свойство Title вашей виртуальной машины также может включать проверку, в то время как свойство Title вашей модели может иметь силу.

Несмотря на то, что виртуальная машина может содержать все свойства модели, существует также возможность наличия валидации (например, заголовок должен быть не пустым), зависимостей данных, связующих свойств пользовательского интерфейса (значков, цвета, кисти и т. д.), которые не являются частью представления.

По существу, все шаблоны пользовательского интерфейса имеют аналогичное «дублирование» в том виде, в котором вы его указываете: именно каскадные изменения. Попробуйте изменить модель в MVC без изменения контроллера.

Это означает, что MVVM (или любой шаблон пользовательского интерфейса, предназначенный для разделения пользовательского интерфейса, логики и состояния) может быть слишком утомительным для простых случаев, таких как ваш пример. Когда логика становится немного больше, чем переход состояния, значение, разделяющее модель контроллера/презентатора/представления, уменьшается.

В вашем конкретном случае, если на самом деле нет каких-либо логических, валидационных или пользовательских свойств, которые ваша виртуальная машина не всплывает, и ваша модель не должна сохраняться, сериализоваться или быть обратно совместимой с существующая структура (или добавление логики для этого в вашей виртуальной машине тривиальна), я бы сильно подумал о объединении M и VM, чтобы избежать создания свойств, единственной целью которых является получение/установка свойств базовой модели.

+2

Я бы никогда, никогда не считал действительность на уровне модели. Основным преимуществом абстрагирования модели является то, что вы можете заменить слой пользовательского интерфейса чем-то другим (другим пользовательским интерфейсом, веб-службой) и повторно использовать код. Если вы делегируете проверку модели оригинальному пользовательскому интерфейсу, модель становится невозможной для правильного повторного использования. –

+0

@ Eric J: Поздний ответ, но вы делаете отличную точку. В большинстве примеров MVVM, предложенных самой инфраструктурой связывания WPF/SL, модели поведенчески светлы (например, модель анемического домена Фаулера) откладывают проверку до времени привязки данных, например. уровень VM. Мне любопытно относиться к вашим мыслям и подходам к привязке данных и ограничению его вторжения в модели домена и в то же время полагаться на модели домена для проверки. – micahtan

+0

@ EricJ., Напротив, должны быть применены инварианты модели домена (совокупности), чтобы обеспечить согласованность модели ** **. Какова цель модели, если она не обеспечивает свою собственную согласованность? –

1

Это интересное замечание ... действительно, часто необходимо изменить ViewModel, чтобы отразить изменения в Модели.

Было бы неплохо, если бы это могло быть автоматическим ... на самом деле, я думаю, что, возможно, благодаря реализации ICustomTypeDescriptor в ViewModel: GetProperties вернет все свойства модели путем отражения. Однако я не уверен, что это будет иметь смысл, потому что модель может не состоять из свойств вообще: это могут быть методы, поля или что-то еще, и не все в модели было бы полезно в ViewModel.

+0

В настоящее время я обрабатываю его с помощью фрагментов и генерации кода, я подозреваю/надеюсь, что MVVM будет больше поддерживать новую версию WPF и Visual Studio, чтобы ViewModels можно было создать, возможно, с помощью Entity Frameworks и т. д., возможно, это уже возможно в EF 4.0? –

+0

«Возможно, это уже возможно в EF 4.0?» : ну, я не видел ничего особенного в MVVM в .NET 4.0 ... Возможно, Microsoft предоставит что-то в виде набора инструментов, как это первоначально было для ASP.NET AJAX, и инструментария WPF (который частично включен в WPF 4) –

0

Я знаю только MVC и в MVC класс Model, содержащий GUI, является некоторой ошибкой. SmartForm, по-видимому, представляет собой форму, что означает, что она не является моделью.Я не знаю, что вы пытаетесь запрограммировать, но я даю вам пример для модели:

Возьмите календарь. Вы можете спросить класс, какую дату он сегодня, какой месяц, сколько дней в месяц, ... У этого нет графического представления. Вид (CalenderViewMonth или whater, который вы хотите) печатает один месяц на экране. Он знает Календер и спрашивает, что писать в разных ячейках.

По существу - возможно, у вас что-то не так в моделировании/понимании MVVM (который является современным .NET-вариантом MVC).


Edit:

Я просто посмотрел MVVM в Википедии. Модель похожа на модель в MVC. Посмотрите, как просмотр в MVC, а также - только графическое представление. ViewModel - это клей между общим представлением и специализированной моделью. Какой-то Адаптер. Не должно быть нарушения DRY.

+0

SmartForm - это просто держатель данных, который определяет, как будет выглядеть форма пользовательского интерфейса. В MVVM View просто подключится к модели, если это возможно, но модель не имеет никакой «привязывающей магии» (OnPropertyChanged , ObservableCollection), и поэтому вам нужно создать ViewModel, который в основном такой же, как у Model, но имеет эти функции привязки, ViewModel, возможно, не придется реализовывать ВСЕ свойства и функциональные возможности модели для определенного вида, но по моему опыту он часто делает и так магию VM = M +. –

+0

Согласен, дублирование здесь происходит из бесполезного слоя (SmartForm). –

3

Простым решением является наличие абстрактного базового класса ViewModel (VM), который предоставляет модель. Вы можете выбрать эту виртуальную машину в сценариях, где это имеет смысл.

т.е.

public abstract class ViewModelBase<T> 
{ 
    public T Model { get; set; } 
} 

Если модель имеет INotifyPropertyChanged реализовать ваше мнение получит событие. Что это делает, так это предоставить вашему представлению доступ ко всем свойствам вашей модели, которые вы не хотите делать несколько раз.

также вы можете использовать недвижимость инициализаторы, как это (который я лично, хранящиеся в фрагментах кода):

public abstract class SampleViewModel 
{ 
    public int MyProperty 
    { 
     get { return Model.MyProperty; } 
     set 
     { 
      Model.MyProperty = value; 
      OnPropertyChanged("MyProperty"); 
     } 
    } 
} 

В большинстве случаев просматриваемых будет один делают изменения в VM и когда это делает какой-либо контроль который связан с этим свойством, тогда будет сказано, что что-то произошло.

Надеюсь, что помогает.

1

Другие предоставили хорошие комментарии о роли компонентов шаблонов MVC/MVVM. Я хотел бы предложить фундаментальное замечание, объясняющее повторяемость, независимо от того, какой шаблон вы выберете.

Как правило, между слоем данных, бизнес-слоем и слоем пользовательского интерфейса будет какое-то повторение. В конце концов, в общем, у вас есть показать каждое свойство для конечного пользователя (UI), модель это поведение (бизнес-уровень) и persist значение (уровень данных).

Как указывали другие, свойство может обрабатываться по-разному на каждом слое, что объясняет фундаментальную потребность в некотором дублировании.

При работе над системами, достаточно большими (или на небольших проектах с правильной командой), я склонен моделировать этот тип информации в UML и использовать генерацию кода (часто в сочетании с частичными классами) для обработки повторяющихся аспектов. В качестве простого примера свойство Last Name может иметь требование (в моей модели UML), чтобы оно ограничивало данные до 50 символов. Я могу сгенерировать код для принудительного применения этого ограничения в свой уровень пользовательского интерфейса (например, физически ограничивая ввод), сгенерировать код на моем бизнес-уровне, чтобы перепроверить это ограничение («никогда не доверяйте пользовательскому интерфейсу»), возможно, выбросив исключение, если данные слишком длинны, и создать мой уровень сохранения (например, столбец NVARCHAR (50), соответствующий файл сопоставления ORM и т. д.).

2012 Обновление

от Microsoft Data Annotations и их поддержка в слое пользовательского интерфейса (например, ASP.Net MVC) и на уровне данных (Entity Framework) проходит долгий путь к реализации многих из проблем, я ранее сгенерированный код для.

1

Одна вещь, которая, кажется, была пропущена здесь и что ваш упрощенный пример не раскрывает тот факт, что ваши представления часто объединяют данные, которые содержатся в нескольких типах моделей домена. В этом случае ваши ViewModels будут содержать ссылки на ряд моделей доменов (разных типов), таким образом объединив кучу связанных данных, которые может просмотреть отдельный вид.

0

Я думаю, что да, ванильный MVVM нарушает DRY. Но я начал the PDX library, который, я думаю, может решить эту проблему во многих случаях. Я написал this post, который, я считаю, решает этот вопрос.

В принципе, моя цель (одна из них) состоит в том, чтобы иметь ViewModels, которые не беспокоятся об уведомлении пользовательского интерфейса. Проект PDX все еще находится в зачаточном состоянии, но если вы читаете этот вопрос, вы можете найти его полезным, и я был бы признателен за любые отзывы, которые могут возникнуть у вас.

1

Эрик Эванс в своей книге «Domain Driven Design» упоминает, что рефакторинг модели не должен быть слишком сложным и что изменение концепции не должно охватывать слишком много модулей, в противном случае рефакторинг Модели становится непомерно высоким, поэтому, если вы спросите меня, если модель типа «скопирована» в ViewModel, безусловно, затрудняет рефакторинг модели.

Эрик упоминает, что следует придавать большему весу сплоченности и изоляции модели, а не чистоте в разделении слоев на основе технических проблем (доступ к базе данных, POCOS, презентация). Что наиболее важно, так это то, что модель домена является хорошим представлением бизнес-домена, поэтому его наиболее важным для модели домена является отдельный изолированный слой и не натянутый в нескольких модулях, чтобы он был легко обновлен (реорганизован).

Учитывая, что было сказано, я бы использовал тот же объект Model в ViewModel, и если бы я хотел уменьшить уровень доступа к объекту модели, я бы «передал» ссылку на Интерфейс, реализованный объектом Model. Например:

// The primary Model classes 
    public partial class OrderItem { 
     public int Id { get; } 
     public int Quantity { get; set; } 
     public Product Item { get; set; } 
     public int Total { get; set; } 
     public void ApplyDiscount(Coupon coupon) { 
      // implementation here 
     } 
    } 

    public partial class Product { 
     public int Id { get; } 
     public string Name { get; set; } 
     public string Description { get; set; } 
     public decimal Price { get; set; }  
    } 

    // The shared views of those model classes 
    public partial class OrderItem : IOrderItemDTO { 
     public IProductDTO Item { 
      get { 
       return this.product; 
      } 
     } 
    } 

    public partial class Product : IProductDTO { 
    } 


    // Separate interfaces... 
    // You enforce the rules about how the model can be 
    // used in the View-ViewModel, without having to rewrite 
    // all the setters and getters. 
    public interface IOrderItemDTO { 
     int Id { get; } 
     int Quantity { get; set; } 
     IProductDTO Item { get; } 
     int Total { get; } 
    } 

    public interface IProductDTO { 
     string Name { get; } 
     string Description { get; } 
     decimal Price { get; } 
    } 

    // The viewmodel... 
    public class OrderItemViewModel { 
     IOrderItemDTO Model { get; set; } 
    }