Подтвердить что ты не робот

MVVM нарушает DRY?

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

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

Итак, моя ViewModel в основном такая же, как моя Model, просто со всеми функциями OnPropertyChanged для привязки к представлению.

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

Это, по-видимому, нарушает основное правило шаблонов Do not Repeat Yourself.

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

4b9b3361

Ответ 1

Я лично не считаю, что это нарушает DRY, поскольку модель и модель представления (я предпочитаю термин презентатор) не указывают на ту же информацию. Например, ваши виртуальные машины и M имеют свойство Title, но ваше свойство заголовка VM также может включать проверку, тогда как свойство модели Title может принимать силу.

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

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

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

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

Ответ 2

Простым решением является наличие абстрактного базового класса 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");
        }
    }
}

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Обновление 2012 года

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

Ответ 5

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

Ответ 6

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

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

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

    // 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; }
    }

Ответ 7

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

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

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


Edit:

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

Ответ 8

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

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