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

MVVM: соединение ViewModel и бизнес-логики

После выполнения нескольких проектов с использованием шаблона MVVM Im все еще борется с ролью ViewModel:

Что я делал в прошлом: Использование модели только в качестве контейнера данных. Ввод логики для управления данными в ViewModel. (Это бизнес-логика?) Con: Логика не может использоваться повторно.

Что я сейчас делаю: Удерживая ViewModel как можно более тонким. Перемещение всей логики в слой модели. Только сохранение представления Логика в ViewModel. Con: делает уведомление пользовательского интерфейса очень болезненным, если данные изменяются внутри слоя модели.

Итак, я дам вам пример, чтобы сделать его более понятным:

Сценарий: Инструмент для переименования файлов. Классы: Файл: представление каждого файла; Правило: содержит логику, как переименовывать файл;

Если Im следующий подход 1: Создание ViewModel для файла, правила и представления → RenamerViewModel. Ввод всей логики в RenamerViewModel: Содержит список FileViewModel и RuleViewModel и продолжающуюся логику. Легко и быстро, но не может использоваться повторно.

Если Im следующий подход 2: Создание нового класса модели → Renamer, который содержит список файлов, правило и продолжающуюся логику для взаимодействия с каждым файлом и применения каждого правила. Создание ViewModel для файла, правила и Renamer. Теперь RenamerViewModel содержит только экземпляр Renamer Model и два ObservableCollections для переноса списка файлов и правил Renamer. Но вся логика находится в модели Renamer. Поэтому, если модель Renamer запускается для управления некоторыми данными по вызовам метода, в ViewModel нет ключа, с помощью которого данные обрабатываются. Поскольку модель не содержит никакого уведомления PropertyChange, и я избегу этого. Таким образом, бизнес и презентационная логика разделены, но это затрудняет уведомление пользовательского интерфейса.

4b9b3361

Ответ 1

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

Ввод логики внутри модели гораздо разумнее, и это прекрасный подход. Каковы недостатки? В вашем вопросе говорится

Итак, если модель Renamer запускается для управления некоторыми данными по методу Вызовы, в ViewModel нет ключа, с помощью которого обрабатываются данные. Потому как Модель не содержит никаких уведомлений PropertyChange, и я избегайте этого.

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

Если вы хотите что-то купить, кто-то должен заплатить за это; если это не модель, которая дает такие уведомления, то у вас останутся только два варианта:

  • Модель просмотра знает, какие операции над моделью (возможно) вызывают изменения и обновляют ее состояние после каждой такой операции.
  • Кто-то еще знает, какие операции вызывают изменения, и они сообщают моделе viewmodel, чтобы обновить его состояние после того, как изменилась оболочка модели.

Первый вариант снова представляет собой плохую идею, потому что по сути она возвращается к созданию "бизнес-логики" внутри модели представления. Не так плохо, как положить всю бизнес-логику в viewmodel, но все же.

Второй вариант более перспективен (и, к сожалению, больше усилий для реализации):

  • Поместите часть своей бизнес-логики в отдельный класс ( "сервис" ). Служба будет выполнять все бизнес-операции, которые вы хотите выполнить, работая с экземплярами модели по мере необходимости.
  • Это означает, что служба знает, когда свойства модели могут измениться (это нормально: модель + сервис == бизнес-логика).
  • Служба предоставит уведомления обо всех моделях с измененными моделями; ваши модели просмотра будут зависеть от службы и получать эти уведомления (чтобы они знали, когда обновлена ​​их "модель" ).
  • Так как бизнес-операции также выполняются службой, это остается очень естественным (например, когда команда вызывается на viewmodel, реакция вызывает соответствующий метод в службе, помните, что сама модель просмотра не знает о бизнесе логика).

Для получения дополнительной информации о такой реализации см. также мои ответы здесь и здесь.

Ответ 2

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

Поскольку модель не содержит никакого уведомления PropertyChange, и я избегу этого

Почему вы избегаете этого? Не поймите меня неправильно, я стараюсь, чтобы мои модели были настолько тупыми, насколько это возможно, но иногда может быть полезно внедрить уведомление о изменениях в вашей модели, и вы выполняете зависимость только от System.ComponentModel, когда вы это делаете. Это полностью агностикский интерфейс.

Ответ 3

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

Пример:

ViewModel:

...

private Person person;

...

string IDataErrorInfo.this[string propertyName]
{
    get
    {
        string error = (person as IDataErrorInfo)[propertyName];
        return error;
    }
}

Модель:

public class Person:INotifyPropertyChanged,IDataErrorInfo
{

...

   string IDataErrorInfo.this[string propertyName]
   {
        get { return this.GetValidationError(propertyName); }
   }

...

   string GetValidationError(string propertyName)
   {
        if(propertyName == "PersonName")
             //do the validation here returning the string error
   }
}

Плюс загляните в шаблон MVCVM, который на самом деле его использует, и довольно хорошо абстрагировать бизнес-логику с классом контроллера вместо модели или viewmodel

Ответ 4

Я делаю следующее

  • Просмотр только с логикой просмотра XAML

  • ViewModel, который обрабатывает обработчики кликов и создает новые модели представлений. Обрабатывает перенаправленные события и т.д.

  • Модель, которая представляет собой контейнер данных и бизнес-логику для проверки данных модели.

  • Службы, которые заполняют модель данными. Например, вызовите веб-сервер, загрузите его с диска, сохраните на диск и т.д. В зависимости от примера, как моя модель, так и сервис будут реализовывать IPropertyChanged. Или они могут иметь обработчики событий.

Любое сложное приложение imo нуждается в другом слое. Я называю модель model + service, view, viewmodel. Служба абстрагирует вашу бизнес-логику и принимает экземпляр модели как зависимость или создает модель.