oop - 在 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
您将被迫覆盖所有接口方法。
解决方案
我不知道这个解决方案是否值得,但您可以使用covariant
关键字和额外的接口来实现与此类似的东西。它需要一个额外的接口,我不完全知道代码是否不那么笨重,但我们开始吧。
编辑:只是想指出您也可以将covariant
关键字放在界面上,基本上允许任何子类EntityIf
收紧类型。
/// 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
}
}