MVVM. Как передать сложные данные / команды из Домена в View - PullRequest
0 голосов
/ 28 августа 2018

Вкратце, вопрос таков: в MVVM (AAC), как Domain (бизнес-логика) может управлять отображением сложных состояний / данных в слое View?

Теперь поподробнее.

Это означает, что внутри Домена: 1) получены, рассчитаны некоторые данные, которые необходимо показать; 2) состояние изменилось, нужно реагировать на это (скрывать / показывать группу виджетов, вызывать новый фрагмент, показывать / обновлять прогресс и т. Д.). И сделать это сложнее, чем просто показать сообщение или диалог или просто отправить LiveData в RecyclerView.

Поэтому примеры типа "привет мир" или "2 + 2 = 4" не подходят, в них все ясно. В MVP это просто сделано. Но здесь я смог найти слабое место MVVM.

Теперь я сделал следующее.

Посредством RxJava2 (в качестве опции это может быть LiveData из AAC) из домена в представление (через ViewModel AAC) передается объект, который содержит тип команды (enum), и имеет несколько полей для данные на все случаи жизни (разные поля для разных команд конечно). И далее, View содержит большой switch-case, в зависимости от типа команды, где все это обрабатывается.

Вариант 2. Чтобы создать кучу конкретных объектов, а затем в виде будет сидеть большой if-instanceof.

Вариант 3. Хранить данные для View в ViewModel AAC (для которых он фактически предназначен) и отправлять из домена только тип команды, затем View получает все необходимые данные из ViewModel.

Вариант 4. Куча (в случае сложных вариантов использования) определенный Observables в Домене и куча subscribers в Представлении.

Итак: Есть ли (если таковые имеются) более элегантный способ? Там может быть какой-то архитектурный паттерн. Может быть, я напрасно размышляю, и это правильный путь (и).

пс . 1) шаблон " Command " здесь не совсем подходит, 2) шаблон " State " уже реализован мной, и он также не решает проблему.

Ответы [ 3 ]

0 голосов
/ 28 августа 2018

Вид модели Архитектура ViewModel

MVVM

  • Вид - это пользовательский интерфейс, макет. В Android это обычно означает Activity, Fragment или ViewHolder и соответствующий ему файл разметки XML.
  • Модель - это наш уровень бизнес-логики, который предоставляет методы для взаимодействия с данными.
  • Модель представления выступает в качестве посредника между представлением и моделью, предоставляя данные из модели через свойства и содержащие состояние пользовательского интерфейса. Кроме того, он определяет команды, которые можно вызывать для таких событий, как щелчки. Модели представлений содержат логику представления вашего приложения.

В архитектурном шаблоне MVVM представление и модель представления в основном взаимодействуют друг с другом посредством привязки данных. В идеале модель представления и представления не должна знать друг о друге. Привязки должны быть связующим звеном между видом и моделью вида и обрабатывать большую часть материала в обоих направлениях. Однако в Android они не могут быть независимыми:

  1. Вы должны сохранить и восстановить состояние, но состояние теперь находится в модели представления.
  2. вам нужно сообщить модели представления о событиях жизненного цикла.
  3. Вы можете столкнуться с ситуациями, когда вам нужно напрямую вызывать методы представления.

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

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

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

Пример:

  <layout xmlns:android="...">
  <data>
    <variable name="vm" type="pkg.MyViewModel" />
  </data>

  <FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:visibility="@{vm.shouldShowText}"
      android:text="@={vm.text}" />

    <Button
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:onClick="@{vm::onButtonClick}"
      android:text="@string/button"/>
  </FrameLayout>
</layout>

Когда вы хотите использовать архитектуру MVVM, ваши макеты должны ссылаться только на одну переменную, конкретную модель представления для этого представления, в данном случае MyViewModel. В модели представления вы предоставляете свойства для макета. Это может быть так же просто, как вернуть String из объекта модели или более сложным, в зависимости от вашего варианта использования.

public class MyViewModel extends BaseObservable {
   private Model model = new Model();

   public void setModel(Model model) {
       this.model = model;
       notifyChange();
   }

   public boolean shouldShowText() {
       return model.isTextRequired();
   }

   public void setText(String text) {
       model.setText(text);
   }

   public String getText() {
       return model.getText();
   }

   public void onButtonClick(View v) {
       // Save data
   }
}

Здесь у нас есть свойство text. Поскольку у нас есть EditText для пользовательского ввода, мы можем использовать двустороннюю привязку данных, чтобы библиотека привязки данных сохраняла входные данные обратно в модель представления. Для этого мы создаем как метод установки, так и метод получения и привязываем свойство к текстовому атрибуту нашего EditText, но на этот раз со знаком = перед скобкой, который сигнализирует библиотеке о том, что здесь требуется двустороннее связывание данных.

Кроме того, мы хотим показывать EditText только тогда, когда наша модель говорит, что требуется ввод текста. Для этого мы предоставляем логическое свойство в нашей модели представления и привязываем его к атрибуту видимости. Чтобы это работало, мы также должны создать адаптер привязки, который устанавливает видимость GONE, когда false, и VISIBLE, когда true.

@BindingAdapter("android:visibility")
public static void setVisibility(View view, boolean visible) {
  view.setVisibility(visible ? View.VISIBLE : View.GONE);
}

Наконец, мы хотим сохранить информацию при нажатии кнопки. Для этого мы создаем команду onButtonClick () в нашей модели представления, которая обрабатывает взаимодействие с моделью. В макете мы привязываем команду к атрибуту onClick кнопки через ссылку на метод. Чтобы это работало напрямую, наш метод должен иметь один параметр типа View, как и OnClickListener. В качестве альтернативы - если вам не нужен параметр View - вы также можете использовать лямбда-выражения прямо в макете. Как видите, связывание данных с моделью представления довольно просто и понятно.

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

0 голосов
/ 08 сентября 2018

Я понял, что мне нужно мигрировать в направлении паттерна MVI.

И нужно реализовать «однонаправленный поток данных».

Очень хорошо это решение описано здесь Реактивные приложения с моделью-представлением-намерением - часть 2 - Представление и намерение по Ханнесу Дорфману

Еще одним интересным решением является библиотека RxPM - Реактивная реализация шаблона Presentation Model в Android

enter image description here

0 голосов
/ 28 августа 2018

В MVP это просто сделано. Но здесь я смог найти слабых точка МВВМ.

Это не слабое место MVVM, это просто разница между реализацией MVP и MVVM.

  • В MVP вы создаете несколько интерфейсов, позволяющих View и Presenter взаимодействовать друг с другом;
  • В MVVM вы создаете посредник (например, LiveData) для соединения View и ViewModel.

ИМХО, вы можете:

  • В вашем UserCase создайте MediatorLiveData A для сохранения результата.
  • В вашей ViewModel создайте MediatorLiveData B для наблюдения A (т.е. MediatorLiveData.addSource(A))
  • В вашем View наблюдайте B , чтобы отразить любые обновления пользовательского интерфейса.

Конкретный пример можно найти в iosched18 .

...