MVVM нарушает СУХОЙ? - PullRequest
39 голосов
/ 08 июня 2009

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

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

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

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

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

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

Ответы [ 8 ]

23 голосов
/ 08 июня 2009

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

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

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

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

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

3 голосов
/ 11 июня 2009

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

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

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

1 голос
/ 02 марта 2012

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

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

Учитывая то, что только что было сказано, я бы использовал тот же объект Model в ViewModel, и если я хочу уменьшить уровень «доступа» к объекту Model, то я бы «передал» ссылку на интерфейс реализуется объектом 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; }
    }
1 голос
/ 19 февраля 2010

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

1 голос
/ 09 декабря 2009

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

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

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

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

2012 Обновление

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

1 голос
/ 08 июня 2009

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

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

0 голосов
/ 11 мая 2011

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

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

0 голосов
/ 08 июня 2009

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

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

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


Edit:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...