在基类Entity中实现工厂方法处理序列化是否属于不良实践?
你的当前实现可以正常运行,但确实存在几个值得关注的问题,属于需要优化的编程实践,具体分析和改进方向如下:
存在的核心问题
1. 硬编码映射的维护负担
你需要手动维护factories这个全局Map,每新增一个Entity子类,都必须手动添加对应的工厂函数到映射中。项目规模越大,越容易出现遗漏,一旦忘记添加就会抛出运行时异常,直接违反了开闭原则(对扩展开放、对修改关闭)。
2. 类型转换的不安全风险
在IDioCrudRepository的get方法中,你使用as E进行强制类型转换。虽然逻辑上你能保证返回类型正确,但编译器无法做编译期校验——如果factories中的映射出现错误(比如把User.fromJson误关联到Product类型),错误只能在运行时暴露,增加了调试成本。
3. 基类职责越界
Entity作为所有实体的基类,核心职责应该是定义实体的通用属性与基础序列化逻辑(比如id、基础json方法)。现在它承担了对象创建工厂的额外职责,违反了单一职责原则,会让基类变得臃肿且难以维护。
推荐的改进方案
方案1:让具体Repository负责实体创建
把实体工厂函数的提供职责交给具体的Repository子类,而非全局映射或基类。这样每个Repository只关心自身对应的实体类型,无需维护全局状态:
abstract class IDioCrudRepository<E extends Entity> extends DioRepository { IDioCrudRepository({required super.baseUrl}); // 子类必须实现该工厂方法 E Function(Map json) get fromJson; FutureOr<E> get(String id) async { final res = await dio.get('$baseUrl/$id'); // 直接调用子类提供的工厂函数,无需强制类型转换 return fromJson(res.data as Map); } } // 具体Product仓库实现 class ProductRepository extends IDioCrudRepository<Product> { ProductRepository({required super.baseUrl}); @override Product Function(Map json) get fromJson => Product.fromJson; }
这种方式的优势:
- 编译期类型安全,彻底避免强制转换的风险
- 新增实体时只需实现对应Repository,无需修改基类或全局映射,符合开闭原则
- 职责清晰分离:
Entity专注实体本身,Repository专注数据获取与实体实例化
方案2:使用代码生成(适合大型项目)
借助json_serializable这类代码生成工具,结合自定义生成逻辑,自动生成实体的工厂映射。这种方式既无需手动维护映射,又能保证类型安全,是大型项目中最推荐的方案。
方案3:反射(谨慎使用)
如果需要保留全局工厂能力,可以用Dart的dart:mirrors反射自动获取子类的fromJson方法,但反射存在明显缺点:
- 会增加包体积,Flutter项目中会影响release模式的编译大小
- 无法在AOT编译环境中正常工作
- 性能略低于直接调用工厂函数
因此反射仅适合特定场景,不推荐作为通用方案。
内容的提问来源于stack exchange,提问作者Alvaro Martinez

