flutter_mobx悟道 dart代理方法的实现

321 阅读3分钟

flutter_mobx简介

flutter_mobx是在flutter上实现的一个状态管理库,相对于其他的状态管理库很轻量,仅仅实现了状态管理,非常容易上手。

flutter_mobx实现状态管理有两种方式:

  1. 手动编写响应式变量,定义可观察变量Observalbe,然后在Action里定义改变Observable值的方法
class Counter {
  Counter() {
    increment = Action(_increment);
  }

  final _value = Observable(0);
  int get value => _value.value;

  set value(int newValue) => _value.value = newValue;
  Action increment;

  void _increment() {
    _value.value++;
  }
}
  1. 使用注解形式生成代码

import 'package:mobx/mobx.dart';

part 'counter.g.dart';

class Counter = CounterBase with _$Counter;

abstract class CounterBase with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

今天要讲的东西就和第二种方式有关。很明显第二种注解方式的代码更简洁、更直观,符合我们的心里预期,编码时我们通常只关心核心的业务代码value++其他诸如Action(_increment)这样的模板代码是能不写就不写。

注解方式生成的代码

程序是严谨的逻辑不是魔法,我们采用注解的形式少写了那么多模板代码,那总会有一个地方多出来一部分代码来实现我们的功能。嗯,就叫他代码守恒定律吧。看一下代码生成的.g文件里做了什么操作让普通的 变量value和方法increment拥有了可观察的属性

  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }

生成的couter.g.dart

mixin _$Counter on CounterBase, Store {
  late final _$valueAtom = Atom(name: 'CounterBase.value', context: context);
  @override
  int get value {
    _$valueAtom.reportRead();
    return super.value;
  }
  @override
  set value(int value) {
    _$valueAtom.reportWrite(value, super.value, () {
      super.value = value;
    });
  }
  late final _$CounterBaseActionController = ActionController(name: 'CounterBase', context: context);
  @override
  void increment() {
    final _$actionInfo = _$CounterBaseActionController.startAction(name: 'CounterBase.increment');
    try {
      return super.increment();
    } finally {
      _$CounterBaseActionController.endAction(_$actionInfo);
    }
  }
}

根据代码守恒,在这里找到了我们用注解方式使用mobx时消失的代码。代码量虽然多,但是好在是通过build_runner自动生成的。

mobx代码生成的精妙之处

回想我们使用注解去使用mobx的时候,仅仅定义了普通变量和普通方法(注解仅在代码生成时作为标记,简单来说是代码生成器是根据有无@observable注解创建可观察变量,@action同理),然后通过注解生成一份包含了可观察变量和方法的代码,就可以让之前定义的普通变量和普通方法实现拥有可观察的功能

仔细看couter.g.dart的方法,他使用mixin关键字混入了CounterBase类,然后Cou nterBase里的属性重写了get/set方法,对于CounterBase里的方法也使用了方法重写。在重写的方法里执行部分特有的逻辑,然后再执行CounterBase里的原逻辑,即通过super.xxx去执行。这对于CounterBase的调用者是无感知的,对于CounterBase自身也是无感知的。couter.g.dart里插入的逻辑是非侵入性的有点像AOP,结合整个代码看我觉得更像是代理模式,通过方法代理实现了逻辑插入。

mobx里的dart的方法代理

讲到这里,你应该也知道了mobx是怎样使用注解生成代理类,通过代理方法简化observable和action的编写了(注解生成代码这期不讲,主要讲他生成的这个代码)

再回顾一下,说明写在注释里了。

//定义了生成的代码路径
part 'counter.g.dart';

///
///这里是关键!!!我们在widget里使用的也是Counter
///这里使用CounterBase混入了生成的_$Counter类完成了代理
///然后赋值给Counter
class Counter = CounterBase with _$Counter;

//我们使用mobx定义的状态类
abstract class CounterBase with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

总结

这种设计对我的启发很大,可以用来做aop,flutter上禁用了反射让我们通过反射去实现AOP成为了不可能。通过注解生成+代理方法也算是另辟蹊径的实现了AOP,并且是编译阶段实现的效率自然高过反射的实现方式,缺点是没有反射灵活。我在实现Repository的时候参考了这种设计,repository在通过数据源获取数据时,repository里仅写核心业务的正常逻辑。由于核心业务是被代理执行的,所以不用管异常,在代理方法的地方进行try-catch统一处理。

可以参考这个示例项目:example_app: 一个示例repository的demo (gitee.com)