Pull to refresh

Comments 4

Какой-то высосанный из пальца пример, как мне кажется. Зачем в представлении делать counter++ и дальше говорить о каком-то UDF? Ведь в реальности во вью будет какой-нибудь
onPressed: controller.like
И что-то вроде
ObxValue(builder, likesObserver)

В любом случае, за «сеттером» controller.like спрячется вся логика. Можно, конечно, спрятать ее за оператором инкремента, чтобы было красиво, но что-то не кажется, что игра стоит свеч.

P.S. И, может быть, не надо вставлять код картинками?

Зачем в представлении делать counter++ и дальше говорить о каком-то UDF? 

Потому что под капотом декоратора находится переопределение оператора ++ (полная мимикрия под стандартный Rx<int>, а как же иначе - это же паттерн "декоратор"), и инкремент значения не изменится напрямую, а только лишь после взаимодействия с логикой модели, посмотрите код в гисте, там кстати полно юнит-тестов и есть пример для понимания работы.
Вкратце поток логики будет такой:

  1. View скомандовала, что хочет инкремента

  2. Сеттер (часть модели) провел операции над этой командой, приняв решение

  3. Значение реактивного поля изменилось

  4. View реактивно перерисовалась

Никак иначе чем через модель, изменение и команда на перерисовку не произойдет. Ровный такой UDF.

Ведь в реальности во вью будет какой-нибудь...

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

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

/// Модель для счетчика лайков.
class LikesController extends GetxController {
  /// Чисто обыгрыш логики.
  static const _isAdmin = true;

  /// Сама переменная, которая не может быть приватной, следовательно
  /// доступна для нарушения инкапсуляции и логики.
  var likeCounter = 0.obs;

  /// Метод изменения переменной.
  void like() {
    if (_isAdmin) {
      likeCounter.value = likeCounter() + 2;
    } else {
      likeCounter.value = likeCounter() + 1;
    }
  }
}

/// Где-то на View
ObxValue(
  (data) => ElevatedButton(
    onPressed: c.like,
    child: Text('Std Likes: $data'),
  ),
  c.likeCounter,
),

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

ObxValue(
  (data) => ElevatedButton(
    // Уппс, ай дид ит эген
    onPressed: () => c.likeCounter.value = c.likeCounter.value + 1,
    child: Text('Std Likes: $data'),
  ),
  c.likeCounter,
),

и вся логика превратится в тыкву.

Давайте теперь применим GetRxDecorator.

/// 4-в-1: Rx-переменная, геттер, сеттер + стрим на всякий случай.
/// Строгая инкапсуляция _значения_ переменной.
final likeCounterDeco = 0.obsDeco(setter: (oldValue, _, __) {
  if (_isAdmin) {
    return oldValue + 2;
  } else {
    return oldValue + 1;
  }
});

А в клиенте будет как-то так

Obx(
  () => ElevatedButton(
    onPressed: c.likeCounterDeco,
    child: Text('Decorator Likes: ${c.likeCounterDeco}'),
  ),
),

Ни при каких случайных условиях вам не удастся нарушить инкапсуляцию и UDF поток данных, ну только если ужасно сильно постараться.

P.S. И, может быть, не надо вставлять код картинками?

Да, зря я так. Надо будет поправить, согласен. Но с другой стороны это же просто сниппеты, а в gist и в pub.dev есть полный код, примеры и тесты.

Это старая история, да, не буду холиварить. Я ж говорю, статья для тех, кто использует GetX именно в плоскости их офигенного State Management. Каждый выбирает для себя. Полтора года использования этой либы на пяти-шести проектах - полет нормальный. Немного пилим напильником, но не принципиально. Если что, мы не понаслышке знаем про Provider, Redux, setState (хехе), так что наш выбор управления состоянием осознанный.

Sign up to leave a comment.

Articles