长模式匹配链的更好解决方案(针对 Dart)
Better solutions to long pattern matching chains (for Dart)
我经常发现自己不得不使用烦人的模式,例如:
ClassA extends BaseClass {
static bool is(val) { ... }
}
ClassB extends BaseClass {
static bool is(val) { ... }
}
...
ClassZ extends BaseClass {
static bool is(val) { ... }
}
BaseClass parser(val) {
if(ClassA.is(val)) {
return ClassA(val);
} else if(ClassB.is(val)) {
return ClassB(val);
...
} else if(ClassZ.is(val)) {
return ClassB(val);
}
}
这很容易出错,需要大量单调的代码。
我想知道是否有一种方法可以以非特定语言(或特定于 Dart 的语言)的方式加快此过程,而不涉及在定义所有模式匹配器后列出它们。我想避免这种情况,因为由于忘记列出已经定义的 class 的模式匹配器之一,我有太多的错误无法计数。
如果你想减少BaseClass.parser()
中的任意条件,你可以使用如下映射:
typedef Specification = bool Function(dynamic val);
typedef Factory = BaseClass Function(dynamic val);
class BaseClass
{
static final Map<Specification, Factory> _factoryMap = {
(val) => val == 'Hello': (val) => ClassA(),
(val) => val == 'There': (val) => ClassB(),
};
static BaseClass? parse(dynamic val)
{
for(var key in _factoryMap.keys)
{
if(key(val)) return _factoryMap[key]!.call(val);
}
throw ArgumentError('No valid factory found!');
}
}
class ClassA extends BaseClass
{ }
class ClassB extends BaseClass
{ }
class ClassC extends BaseClass
{ }
在Python中,可以扩展type
,注册子类自己的规范方法,不用像这样手动列出。但是我暂时还不知道 Dart 中有这样的运行时元编程。也许你可以在 Dart 中使用 source_gen 来自动生成条件。
将检查与对象的构造相结合会略有帮助。这将减少意外使用错误检查和构造函数的可能性,并且将减少您需要在 class 定义之外添加的代码量:
ClassA extends BaseClass {
/// Attempts to return a [ClassA] if possible.
///
/// Returns `null` if inappropriate.
static ClassA? tryFrom(dynamic val) { ... }
}
ClassB extends BaseClass {
static ClassB? tryFrom(dynamic val) { ... }
}
...
ClassZ extends BaseClass {
static ClassZ? tryFrom(dynamic val) { ... }
}
BaseClass parser(dynamic val) {
BaseClass object = ClassA.tryFrom(val) ??
ClassB.tryFrom(val) ??
... ??
ClassZ.tryFrom(val);
if (object == null) {
// Throw some exception here.
}
return object;
}
从那里,您可以让 parser
使用循环:
typedef TryFromFunction = BaseClass? Function(dynamic);
final tryFromFunctions = [
ClassA.tryFrom,
ClassB.tryFrom,
...
ClassZ.tryFrom,
];
BaseClass parser(dynamic val) {
for (var tryFrom in tryFromFunctions) {
var object = tryFrom(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
这并不能免除您在添加新 class 时更新其他位置的责任,但是:
- 工作量很小。
- 您也许 可以添加单元测试来检查列表是否已更新。例如,如果
ClassA
、ClassB
、...、ClassZ
中的每一个都在一个单独的文件中,可以通过路径或文件名轻松区分,那么您可以进行一个测试来验证 tryFromFunctions.length
匹配这些文件的数量。
目前,我使用的解决方案取自所列的其他解决方案并有所不同。基本上,我像其他人一样创建了一个匹配器列表,但在 class 本身中添加了匹配器。基本上,
List<BaseClass Function(dynamic)> _matchers = [];
bool addMatcher(bool Function(dynamic) matcher, BaseClass Function(dynamic) factory) {
_matchers.add((val) {
return matcher(val) ? factory(val) : null;
});
return true;
}
class ClassA extends BaseClass {
static final _ = addMatcher(matches, (val) => ClassA(val));
ClassA(val) { ... }
static bool matches(val) { ... }
}
class ClassB extends BaseClass {
static final _ = addMatcher(...);
...
}
...
BaseClass parser(val) {
for (var matcher in _matchers) {
var object = matcher(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
这背后的原因是我可以很容易地验证 class 正在被检查。不幸的是,我仍然不确定如何自动执行此操作,因为这只是一个快速而肮脏的解决方案。如果我最终实现了自动 generation/verification.
的源代码生成或单元测试,我可能会通过更新回到这里。
我经常发现自己不得不使用烦人的模式,例如:
ClassA extends BaseClass {
static bool is(val) { ... }
}
ClassB extends BaseClass {
static bool is(val) { ... }
}
...
ClassZ extends BaseClass {
static bool is(val) { ... }
}
BaseClass parser(val) {
if(ClassA.is(val)) {
return ClassA(val);
} else if(ClassB.is(val)) {
return ClassB(val);
...
} else if(ClassZ.is(val)) {
return ClassB(val);
}
}
这很容易出错,需要大量单调的代码。 我想知道是否有一种方法可以以非特定语言(或特定于 Dart 的语言)的方式加快此过程,而不涉及在定义所有模式匹配器后列出它们。我想避免这种情况,因为由于忘记列出已经定义的 class 的模式匹配器之一,我有太多的错误无法计数。
如果你想减少BaseClass.parser()
中的任意条件,你可以使用如下映射:
typedef Specification = bool Function(dynamic val);
typedef Factory = BaseClass Function(dynamic val);
class BaseClass
{
static final Map<Specification, Factory> _factoryMap = {
(val) => val == 'Hello': (val) => ClassA(),
(val) => val == 'There': (val) => ClassB(),
};
static BaseClass? parse(dynamic val)
{
for(var key in _factoryMap.keys)
{
if(key(val)) return _factoryMap[key]!.call(val);
}
throw ArgumentError('No valid factory found!');
}
}
class ClassA extends BaseClass
{ }
class ClassB extends BaseClass
{ }
class ClassC extends BaseClass
{ }
在Python中,可以扩展type
,注册子类自己的规范方法,不用像这样手动列出。但是我暂时还不知道 Dart 中有这样的运行时元编程。也许你可以在 Dart 中使用 source_gen 来自动生成条件。
将检查与对象的构造相结合会略有帮助。这将减少意外使用错误检查和构造函数的可能性,并且将减少您需要在 class 定义之外添加的代码量:
ClassA extends BaseClass {
/// Attempts to return a [ClassA] if possible.
///
/// Returns `null` if inappropriate.
static ClassA? tryFrom(dynamic val) { ... }
}
ClassB extends BaseClass {
static ClassB? tryFrom(dynamic val) { ... }
}
...
ClassZ extends BaseClass {
static ClassZ? tryFrom(dynamic val) { ... }
}
BaseClass parser(dynamic val) {
BaseClass object = ClassA.tryFrom(val) ??
ClassB.tryFrom(val) ??
... ??
ClassZ.tryFrom(val);
if (object == null) {
// Throw some exception here.
}
return object;
}
从那里,您可以让 parser
使用循环:
typedef TryFromFunction = BaseClass? Function(dynamic);
final tryFromFunctions = [
ClassA.tryFrom,
ClassB.tryFrom,
...
ClassZ.tryFrom,
];
BaseClass parser(dynamic val) {
for (var tryFrom in tryFromFunctions) {
var object = tryFrom(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
这并不能免除您在添加新 class 时更新其他位置的责任,但是:
- 工作量很小。
- 您也许 可以添加单元测试来检查列表是否已更新。例如,如果
ClassA
、ClassB
、...、ClassZ
中的每一个都在一个单独的文件中,可以通过路径或文件名轻松区分,那么您可以进行一个测试来验证tryFromFunctions.length
匹配这些文件的数量。
目前,我使用的解决方案取自所列的其他解决方案并有所不同。基本上,我像其他人一样创建了一个匹配器列表,但在 class 本身中添加了匹配器。基本上,
List<BaseClass Function(dynamic)> _matchers = [];
bool addMatcher(bool Function(dynamic) matcher, BaseClass Function(dynamic) factory) {
_matchers.add((val) {
return matcher(val) ? factory(val) : null;
});
return true;
}
class ClassA extends BaseClass {
static final _ = addMatcher(matches, (val) => ClassA(val));
ClassA(val) { ... }
static bool matches(val) { ... }
}
class ClassB extends BaseClass {
static final _ = addMatcher(...);
...
}
...
BaseClass parser(val) {
for (var matcher in _matchers) {
var object = matcher(val);
if (object != null) {
return object;
}
}
// Throw some exception here.
}
这背后的原因是我可以很容易地验证 class 正在被检查。不幸的是,我仍然不确定如何自动执行此操作,因为这只是一个快速而肮脏的解决方案。如果我最终实现了自动 generation/verification.
的源代码生成或单元测试,我可能会通过更新回到这里。