首页 > 解决方案 > 在 dart 中隐藏扩展类的接口

问题描述

我正在尝试创建一个验证层,它将对域层实体中业务逻辑方法的调用包装起来。

Validator 必须与 Entity 具有相同的接口,并且可以访问 Entity 所持有的状态。

但是,验证器接口方法的类型签名需要与实体不同,因为验证器可以验证和转换来自 UI 的输入(例如)。Validator 还需要将这些输入验证/转换调用和底层业务逻辑方法调用包装在 try catch 中。

这是我当前实现的一个示例:

class Entity {
  // state
  int _num;
  int get num => _num;

  // init the state
  Entity(this._num = 0)

  // business logic methods
  void incrementBy(int n) {
    // business logic validation
    if (n <= 0){
      throw Exception('[n] must be greater than 0'); // shouldn't throw raw Exceptions in general
    }
    // business logic
    _num += n;
  }
}

class Validator {
  // have to hold an instance of the entity
  final Entity _entity;

  Validator(this._entity);

  // have to copy the getters in the entity class
  int get num => _entity.num;

  // same interface as the Entity, but different type signature
  void incrementBy(String n) {
    try {
      // validate user input
      final inc = ConvertToInt(n); // -> could throw a FormatException
      // call the underlying busines logic
      _entity.incrementBy(inc); // -> could throw an Exception
    } on Exception catch (e) { // shouldn't catch raw Exceptions in general
      ...
    }
}

有没有更好的方法来包装实体?

以上面显示的方式执行此操作感觉非常笨拙,因为没有强制执行哪些方法需要重写,就像implementing实体的情况一样,您不能这样做,因为类型签名必须相同。

像这样的东西class Validator hides Entity{...}会很棒。它类似于 an 的组合extends,您不需要持有实体的实例或重新实现 getter,并且implements您将被迫覆盖所有接口方法。

标签: oopflutterdart

解决方案


我不知道这个解决方案是否值得,但您可以使用covariant关键字和额外的接口来实现与此类似的东西。它需要一个额外的接口,我不完全知道代码是否不那么笨重,但我们开始吧。

编辑:只是想指出您也可以将covariant关键字放在界面上,基本上允许任何子类EntityIf收紧类型。

这是下面代码的 Dart Pad 链接

/// This is the common interface between the entity
/// and the validator for the entity. Both need to
/// implement this.
abstract class EntityIf {

  // Private factory constructor to disallow
  // extending this class
  EntityIf._();

  // We use 'dynamic' as the type for [num].
  // We'll enforce type later using the
  // 'covariant' keyword
  dynamic get num;

  // Same here, type is dynamic
  void incrementBy(dynamic value);
}

class Entity implements EntityIf {
  Entity(this._num);

  int _num;

  // Getters don't need the covariant keyword for some reason ?!? I'm not complaining!
  @override
  int get num => _num;

  // Here we see the covariant keyword in action.
  // It allows restricting to a more specific type
  // which is normally disallowed for overriding methods.
  @override
  void incrementBy(covariant int value) {
    _num += value;
  }
}

class ValidatorForEntity implements EntityIf {
  // Validator still needs to wrap the entity, coudln't
  // figure out a way around that
  ValidatorForEntity(this._entity)
    : assert(_entity != null);

  final Entity _entity;

  @override
  dynamic get num => _entity.num;

  // Validator just overrides the interface with no
  // covariant keyword.
  @override
  void incrementBy(dynamic value) {
    assert(value != null);

    int finalValue = int.tryParse(value.toString());
    if (finalValue == null) {
      throw '[value] is not an instance of [int]';
    }

    // int type will be enforced here, so you can't
    // create validators that break the entity
    _entity.incrementBy(finalValue);
  }
}

void main() {
  final x = ValidatorForEntity(Entity(0));

  x.incrementBy(1);
  print(x.num); // prints 1

  x.incrementBy('1');
  print(x.num); // prints 2

  try {
    x.incrementBy('a');
  } catch (e) {
    print('$e'); // should give this error
  }
}

推荐阅读