Скрытие интерфейса расширенного класса в дротике - PullRequest
0 голосов
/ 17 февраля 2020

Я пытаюсь создать слой проверки, который оборачивает вызовы методов бизнес-логики c в сущностях на уровне домена.

Валидатор должен иметь тот же интерфейс, что и сущность, и предоставлять доступ к состояние, которое содержит объект.

Однако сигнатуры типов методов интерфейса валидатора должны отличаться от сущностей, поскольку валидатор может проверять и преобразовывать входные данные из пользовательского интерфейса (например). Валидатору также необходимо обернуть эти входные вызовы проверки / преобразования и базовый вызов бизнес-логики c в try-перехватах.

Это пример моей текущей реализации:

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
      ...
    }
}

Is Есть ли лучший способ обернуть сущность?

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

Что-то вроде class Validator hides Entity{...} было бы здорово. Это будет что-то вроде комбинации extends, вам не нужно будет содержать экземпляр сущности или переопределения методов получения, и implements, поскольку вы будете вынуждены переопределить все методы интерфейса.

1 Ответ

0 голосов
/ 20 февраля 2020

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

Редактировать: Просто хочу указать, что вы также можете разместить 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
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...