0

我正在寻找一种验证冻结模型的好方法。到目前为止,我想出了三种方法,如下面的代码片段所示。

@freezed
class Options with _$Options {
  Options._();

  factory Options._internal({required List<String> languages}) = _Options;

  // #1: validation in factory constructor
  factory Options({required List<String> languages}) {
    if (languages.isEmpty) {
      throw Exception('There must be at least one language.');
    }

    return Options._internal(languages: languages);
  }

  // #2: expose mutation methods with built-in validation
  Options changeLanguages(List<String> languages) {
    if (languages.isEmpty) {
      throw Exception('There must be at least one language.');
    }
    return copyWith(languages: languages);
  }

  // #3: validation using custom properties
  late final List<Exception> validationResult = <Exception>[
    if (languages.isEmpty) Exception('There must be at least one language.'),
  ];

  // #4: validation using a custom method
  void validate() {
    if (languages.isEmpty) {
      throw Exception('There must be at least one language.');
    }
  }
}

#1:工厂构造函数中的验证。不幸的是,这只适用于新创建的对象,并且需要对copyWith.

#2:变异方法中的验证。除了#1之外,这还可以用于在对象创建后运行验证,但仍然不适用于copyWith.

#3:暴露带有验证错误的属性。到目前为止,这是我最喜欢的方法,尽管它要求模型的用户明确查找错误。

#4:#3的变体,它使用抛出方法而不是提供错误列表。

您对此有何看法?您是否知道任何更好的方法,或者是否有我忽略的包 API 的一部分?

4

2 回答 2

0

我可能会将验证移到下面一步。您可以创建一个模型LanguageList

class LanguageList {
  LanguageList(this.data) {
    if (data.isEmpty) throw ArgumentError('Provide at least one element');
  }
  
  final List<String> data;

  @override
  bool operator ==(Object other) => other is LanguageList && ListEquality<String>.equals(data, other.data);

  @override
  int get hashCode => DeepCollectionEquality().hash(data);
}

并在Options模型中使用它:

factory Options._internal({required LanguageList languages}) = _Options;

您甚至可以通过使非法状态无法表示而不是在运行时抛出错误来使其更加“编译友好”:

class LanguageList {
  LanguageList(String head, Iterable<String> tail) : data = [head, ...tail];
  
  final List<String> data;

  @override
  bool operator ==(Object other) => other is LanguageList && ListEquality<String>.equals(data, other.data);

  @override
  int get hashCode => DeepCollectionEquality().hash(data);
}

在这种情况下,没有办法创建错误的实例。

于 2021-04-05T18:47:12.360 回答
0

Freezed 在 v0.12.0 中添加了对自定义asserts 的支持:https : //pub.dev/packages/freezed#asserts。将这些应用于您的示例会产生以下结果:

@freezed
abstract class Options with _$Options {
  Options._();

  @Assert('languages.isNotEmpty', 'There must be at least one language.')
  factory Options({required List<String> languages}) = _Options;
}

但是,这不允许您抛出任意异常,并且asserts 仅包含在调试版本中,而不包含在配置文件/发布版本中。

于 2021-04-05T12:16:27.723 回答