Dart中的类引用类型(元类)及Pascal工厂模式迁移实现
从Object Pascal类工厂模式到Dart的转换方案
1. 基础实现(对应你Pascal的类型引用+枚举工厂)
先定义基类与子类:
abstract class BaseClass { void doSomething(); } class DerivedClass1 extends BaseClass { @override void doSomething() => print('DerivedClass1 working'); } class DerivedClass2 extends BaseClass { @override void doSomething() => print('DerivedClass2 working'); }
对应Pascal的枚举标记:
enum ClassType { derived1, derived2 }
Dart没有直接的类引用类型,但可以用构造函数引用作为Map的值,替代Pascal的类引用数组:
// 枚举到构造器的映射,比数组更安全 final _classConstructors = <ClassType, BaseClass Function()>{ ClassType.derived1: DerivedClass1.new, // 语法糖:直接引用构造函数 ClassType.derived2: DerivedClass2.new, };
工厂函数实现:
BaseClass createClassInstance(ClassType type) { final constructor = _classConstructors[type]; if (constructor == null) { throw ArgumentError('Unsupported type: $type'); } return constructor(); }
调用示例:
void main() { final inst1 = createClassInstance(ClassType.derived1); inst1.doSomething(); // 输出 DerivedClass1 working }
2. Dart专属语法糖简化
扩展方法直接从枚举生成实例
给枚举加扩展,省去单独的工厂函数:
extension ClassTypeExtension on ClassType { BaseClass createInstance() { return _classConstructors[this]!(); // 也可改用switch避免Map查找: // switch (this) { // case ClassType.derived1: return DerivedClass1(); // case ClassType.derived2: return DerivedClass2(); // } } } // 调用更直观: final inst = ClassType.derived2.createInstance();
构造函数引用语法
Dart中Class.new可以直接获取默认构造函数的引用,比() => Class()更简洁,已在上述Map中应用。
3. 引用数组转Dart Map的方案
你Pascal里的类引用数组,在Dart里用**Map<ClassType, 构造函数引用>**是最优替代——数组依赖枚举的索引顺序,一旦枚举新增/调整顺序,数组必须同步修改,极易出错;而Map用枚举值作为键,类型安全且不受顺序影响。
如果一定要模拟数组(不推荐),可以用枚举的index作为数组下标:
final _constructorArray = [DerivedClass1.new, DerivedClass2.new]; BaseClass createFromArray(ClassType type) => _constructorArray[type.index]();
这种方式风险高,仅适合固定不变的枚举类型。
4. 关于枚举“多余”但怕出错的问题
枚举的核心价值是类型安全的标记,避免用字符串/整数作为标识时的拼写错误、越界问题。如果担心枚举和构造器的映射出错,可以做以下优化:
编译时检查
启动时验证所有枚举值都有对应的构造器,提前暴露问题:
void _validateConstructors() { for (final type in ClassType.values) { if (!_classConstructors.containsKey(type)) { throw StateError('Missing constructor mapping for $type'); } } } // 在main开头调用 void main() { _validateConstructors(); // ...业务代码 }
代码生成(适合子类较多的场景)
用build_runner配合自定义注解自动生成枚举和构造器映射,完全避免手动编写的错误。比如给子类加@FactoryMarker注解,再通过代码生成工具自动生成枚举和对应的Map。
替代方案:密封类
如果实在不想用枚举,也可以用密封类做标记,但写法更繁琐:
sealed class ClassMarker {} class Derived1Marker extends ClassMarker {} class Derived2Marker extends ClassMarker {} final _markersToConstructors = <Type, BaseClass Function()>{ Derived1Marker: DerivedClass1.new, Derived2Marker: DerivedClass2.new, }; BaseClass createInstance(ClassMarker marker) => _markersToConstructors[marker.runtimeType]!();
调用时需要传Derived1Marker(),不如枚举简洁。
内容的提问来源于stack exchange,提问作者Rhianne Moll
相关产品推荐
相关产品推荐

