Pull to refresh
18
0
Валерий @rookie_cruekie

Программист

Send message

Прекрасно, когда есть из чего выбрать. Мне фломастеры из rxdart-коробки больше по душе, потому что я так или иначе последние три года пользуюсь ReactiveX в составе RxJava, RxQt, RxCpp в той или иной степени, с их подкупающей мощью обработки данных посредством цепочек операторов, и поэтому я убиваю двух зайцев, используя rxdart как управление состоянием UI и для работы с данными.

Ну, во-первых, насколько часто нам приходится писать счетчики? Начиная даже самый простой проект, потихоньку приходишь к усложнению. А вот представьте, Вам потребовалось вставить обратный отсчет для блокировки кнопки между нажатиями. Обойдетесь без фонового потока? А если нет, как будете реализовывать?
Позвольте представить решение в рамках концепции данной статьи:


/// делаем задержку в 3 секунды, с уведомлением через каждую секунду.
class _Counter {
  int _count;
  /// Счетчик обратного отсчета
  int _countdown = 0;

  int get count => _count;

  _Counter(this._count)
      : this.onCounterUpd = BehaviorSubject<int>.seeded(_count),
        this.onCountdownUpd = BehaviorSubject<int>.seeded(0);

  final BehaviorSubject<int> onCounterUpd;

  /// Евент обратного отсчета
  final BehaviorSubject<int> onCountdownUpd;

  /// Вынесем инкремент за пределы виджета, добавим генерацию события.
  Future incrementCounter() async {
    if(_countdown <= 0) {
      onCounterUpd.add(++_count);
      /// Запуск таймера, с вочдогом и генерацией евентов.
      _countdown = 3;
      onCountdownUpd.add(_countdown);
      Observable
          .periodic(Duration(seconds: 1), (_) => --_countdown)
          .take(3)
          .listen((e) => onCountdownUpd.add(_countdown));
    }
  }
}

после чего делаем FAB реактивным,


      /// Кнопка стала реактивной
      floatingActionButton: StreamBuilder<int>(
        initialData: _counter.onCountdownUpd.value,
        stream: _counter.onCountdownUpd,
        builder: (context, snapshot) {
          return FloatingActionButton(
            onPressed: snapshot.data <= 0 ? _counter.incrementCounter : null,
            tooltip: 'Increment',
            backgroundColor: snapshot.data <= 0
                ? Theme.of(context).primaryColor
                : Colors.grey,
            child: Icon(Icons.add),
          );
        }
      ), // This trailing comma makes auto-formatting nicer for build methods.

и добавляем рекативное же упоминание


            /// Реактивная надпись
            StreamBuilder<int>(
                stream: _counter.onCountdownUpd,
                builder: (context, snapshot) {
                  return Text(
                    'Rest ${snapshot.data} seconds',
                    style: Theme.of(context).textTheme.title,
                  );
                }),

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

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


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


MVI я не использую в чистом виде по этой же причине, но мне импонирует immutable-way, и передача упакованного состояния в одном флаконе. Позже я черкану статью о своей реализации.

в процессе. Есть прикольная архитектура MVI, я ее применяю и в процессе слегка модернизирую. Вот спустя месяцок накидаю статью с реальным примером.

Да, согласен, косяк. Даже два.


  1. Поправимо
  2. Не отменяет самого принципа и эффективности применения

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

Обратите внимание, это для простоты восприятия. Никто не мешает вам сделать класс, и убрать его в business-слой.


class _Counter {
  int count;

  _Counter(this.count);

  /// 3. Создадим евент.
  final onCounterUpd = BehaviorSubject<int>();

  /// 4. Вынесем инкремент за пределы виджета, добавим генерацию события.
  Future incrementCounter() async {
    onCounterUpd.add(++count);
  }
}

Спасибо, добавлю. К сожалению, нет возможности проверить, могу только положиться на Вашу информацию.

Да, просто добавьте строки


 "pushingFew": "You pressed few times",
  "pushingMany": "You pressed many times",

В i18n.dart появится


    switch (cnt.toString()) {
      case "0":
        return "You pressed NONE (";
      case "1":
        return "You pressed once";
      case "2":
        return "You pressed twice";
      case "few":
        return "You pressed few times";
      case "many":
        return "You pressed many times";
      default:
        return "You pressed $cnt times";
    }

и можно использовать


S.of(context).pushing('many'),
S.of(context).pushing('few'),

Думаю да, опечатка, я вроде аккуратно переводил. Возможно, для автора английский не совсем прям, как родной.
Два момента:


  1. Опыт, который упоминается в статье — это опыт не мой, а автора. Но, ссылаясь на лично свой, то для примера выделение слоев абстракций для работы с БД, файловыми системами помогло мне лишь однажды из 10+ проектов. Так что да — в моем случае это была лишняя работа. Особенно это касается работы с Android приложениями, где локально sqlite вообще безальтернативна.
  2. На самом деле эта статься интересна, как стартовая для описания dart-библиотеки rx_command, и после утомительного boilerplate InheritedWidgets & BLoC для меня это действительно находка. Вот, на днях переведу остальное.
Ну, автор высказывается по поводу избыточного вертикального «расслоения», а эта библиотека — фундамент для Managers, другими словами, разделение архитектуры по горизонтали.
будет здорово

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

Интересно. В этом что-то есть. Я вдруг подумал, что парочка дополнительных евентов для UndoWatcher: cmdAdded/cmdRemoved позволила бы очень элегантно реализовать что-то вроде этого. В принципе, и сейчас можно, но достаточно коряво. Думаю, пойдет в wish-list.

Наверное, следует рассматривать разные подходы. Делать профилирование каждой операции. Некоторые можно заменить "действиями", а где-то возможно, придется сбрасывать 20 метров на диск. Сразу так и не скажешь. Специфика в том, битмапы, это не векторая графика, тут после ряда undo/redo можно такое на выходе получить…
На первый взгляд, все-таки лучше сохранять действия — выглядит так, словно это будет компактнее и быстрее загружаться.
К примеру


  • CropUndo: хранит координаты прямоугольника обрезки. Redo() — применяет обрезку, сохраняя обрезанное, Undo() — возвращает обрезанное, совершая операцию "склеивания"
  • BlurUndo: сохраняет процент размытия. Redo() — накладывает маску (слой) с размытием на текущий битмап, Undo() — удаляет маску (слой)
  • StretchUndo: с растягиваниями уже посложнее, Undo() вряд ли вернет прежнее качество

Я бы поинтересовался на профильных форумах, присмотрелся к Фотошопу. Не пользуюсь им уже более 10 лет, со времен Photoshop v.6. Там довольно шустро все было.

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

Вы верно отметили, Qt-шный фреймворк был для меня стартовой точкой, с ним я работал лет пять, наверное. Отличное решение, я в него добавлял сериализацию (так для ясности я назову store/restore) для проектов нашей группы, а так же версионность. Макросы там реализованы неправильно, имхо. Пришлось поправлять.
Именно это все я собрал воедино в Java, так как на сегодня Java/JavaFx/Android — мое направление развития.
UndoWatcher всего лишь qt-шные signals/slots, реализованные средствами Java. Это read-only информация о происходящем со стеком. QUndoStack, раз уж Вы его упомянули, именно так сигналит клиентам о своем состоянии.
А так-то да, на вкус и цвет, как говорится.

2

Information

Rating
Does not participate
Location
Екатеринбург, Свердловская обл., Россия
Date of birth
Registered
Activity