Dart中如何在同一类的多个构造函数间复用相同assert断言
Dart多构造函数重复断言复用方案
当然有可行方案,Dart构造函数的重复断言逻辑有两种成熟的复用方式,你可以根据是否需要支持const构造选择对应方案:
方案1:工厂构造转发到统一私有生成构造(无const需求时首选)
把所有公共断言、公共字段初始化逻辑全部收敛到一个私有的生成构造函数里,对外暴露的默认构造、命名构造全部改为工厂构造,各自完成差异化的参数解析、处理后,统一转发到这个私有构造完成实例化。
class Foo { final String name; final int age; // 所有公共断言只在此处写一次,所有实例化逻辑最终都会走到这里 Foo._internal({required this.name, required this.age}) : assert(name.isNotEmpty, "名称不能为空"), assert(age >= 0, "年龄不能为负数"), assert(age < 150, "年龄参数不合法"); // 对外默认构造 factory Foo({required String name, required int age}) { // 可写默认构造专属的参数处理逻辑,比如默认值填充 return Foo._internal(name: name, age: age); } // 命名构造fromForm factory Foo.fromForm(Map<String, dynamic> form) { // 可写表单场景专属的参数解析、转换逻辑 final parsedName = form['userName'] as String; final parsedAge = int.tryParse(form['userAge'] ?? '') ?? 0; return Foo._internal(name: parsedName, age: parsedAge); } }
这个方案的优势:
- 公共断言完全不会重复,后续新增/修改断言只需要改私有构造一处
- 新增命名构造时只要转发到私有构造,不会漏写校验逻辑
- 断言执行时机和直接写在构造初始列表完全一致,在实例完全初始化前就会完成校验,不会出现半初始化实例泄露的问题
注意:Dart中工厂构造不能加const修饰,如果你的类需要支持const编译时常量构造,用下面的方案2。
方案2:抽离静态校验方法(支持const构造场景)
把公共断言逻辑抽成类的静态方法,固定返回true,所有构造的初始列表里直接在assert中调用这个静态方法即可。如果静态方法加const修饰,完全不会影响const构造的支持。
class Foo { final String name; final int age; // 抽离公共断言为静态校验方法 static bool _validateCommon(String name, int age) { assert(name.isNotEmpty, "名称不能为空"); assert(age >= 0, "年龄不能为负数"); assert(age < 150, "年龄参数不合法"); return true; } // 默认构造直接调用校验方法 const Foo({required this.name, required this.age}) : assert(_validateCommon(name, age)); // 命名构造解析完参数后调用同一个校验方法 const Foo.fromForm(Map<String, dynamic> form) : name = form['userName'] as String, age = form['userAge'] as int, assert(_validateCommon(form['userName'] as String, form['userAge'] as int)); }
这个方案的注意点:如果字段是在构造初始列表中完成赋值的(比如上面fromForm里的name、age直接从入参map解析),需要把解析后的值传给校验方法,不能直接引用未完成初始化的实例字段。
避坑提醒
不要把公共断言写到构造函数体里:Dart构造函数体执行时,实例字段已经完成初始化,这时候如果断言失败,可能已经有半初始化的实例被引用传递,和初始列表中断言的执行时机、安全表现不一致,会引入潜在问题。
内容的提问来源于stack exchange,提问作者CbL
相关产品推荐
相关产品推荐

