如何结合Flutter与Riverpod实现策略模式及算法注册方案?
如何在Riverpod中优雅注册策略模式的算法实现
我在Flutter应用里有个页面负责加载指定主题的数据,主题里包含用户交互时要用的算法标识。随着算法数量增加,用策略模式来根据标识选择对应实现很合适。现在用Riverpod做状态管理,已经写了初步的Provider代码:
abstract interface class TestAlgorithm { String foo(); } @riverpod class AlgorithmMap extends _$AlgorithmMap { @override Map<String, TestAlgorithm> build() { return {}; // 这里不知道怎么填充 } // 是不是可以这么做? void registerAlgorithm(String algorithmKey, TestAlgorithm algorithm) { final nextState = { ...state }; nextState[algorithmKey] = algorithm; state = nextState; } } @riverpod TestAlgorithm? algorithm(AlgorithmRef ref, String algorithmKey) { final algorithmMap = ref.watch(algorithmMapProvider); return algorithmMap[algorithmKey]; }
组件可以通过监听algorithmProvider调用算法的foo方法,但我纠结怎么注册这些算法,目前有两个思路:
- 在
AlgorithmMap的build方法里手动构建映射:简单直接,但加新算法就得改Provider,违反开闭原则; - 用自定义注解+
dart:mirrors扫描注册:能动态生成列表,不用改Provider,但会带来性能损耗和包体积变大的问题。
我想知道有没有注解生成器自动生成注册代码的方案?新增算法时跑生成器就能更新列表,兼顾两种方案的优点又避开缺点。或者还有其他更优的实现思路?
最优方案:用注解生成器自动生成注册代码
这完全可行,而且是兼顾开闭原则和性能的最佳方案——核心是用build_runner+source_gen在编译期生成注册代码,完全不会有运行时反射的开销。
具体实现步骤
- 定义自定义注解
先创建一个注解类,用来标记需要注册的算法实现:
class AlgorithmKey { final String key; const AlgorithmKey(this.key); }
- 给算法实现添加注解
每个算法实现类加上这个注解,指定对应的标识:
@AlgorithmKey("algorithm_a") class AlgorithmA implements TestAlgorithm { @override String foo() => "Result from Algorithm A"; } @AlgorithmKey("algorithm_b") class AlgorithmB implements TestAlgorithm { @override String foo() => "Result from Algorithm B"; }
- 编写代码生成器
创建一个生成器类,扫描所有带AlgorithmKey注解的类,生成对应的注册代码:
class AlgorithmGenerator extends GeneratorForAnnotation<AlgorithmKey> { @override FutureOr<String> generate(BuildStep buildStep) async { final entries = <String>[]; await for (final library in buildStep.findLibraryAssets()) { final libraryElement = await buildStep.resolver.libraryFor(library); for (final element in libraryElement.topLevelElements) { if (element is ClassElement) { final annotation = element.metadata.firstWhere( (m) => m.isConstant && m.type.name == "AlgorithmKey", orElse: () => null, ); if (annotation != null) { final reader = ConstantReader(annotation.constantValue); final key = reader.read("key").stringValue; entries.add(" '$key': ${element.name}(),"); } } } } // 生成完整的映射表代码 return """ // 自动生成的算法注册代码,请勿手动修改 import 'package:your_app/algorithm_interface.dart'; import 'package:your_app/algorithms.dart'; Map<String, TestAlgorithm> generateAlgorithmMap() { return { ${entries.join("\n")} }; } """; } }
- 配置build_runner
在pubspec.yaml里添加依赖:
dependencies: source_gen: ^1.4.0 riverpod: ^2.3.0 # 其他业务依赖 dev_dependencies: build_runner: ^2.4.0 # 其他开发依赖
再创建build.yaml配置生成器:
targets: $default: builders: your_app|algorithm_generator: enabled: true
- 生成代码并在Riverpod中使用
运行flutter pub run build_runner build生成generate_algorithm_map.dart,然后修改AlgorithmMap的build方法:
@riverpod class AlgorithmMap extends _$AlgorithmMap { @override Map<String, TestAlgorithm> build() { // 使用自动生成的映射表 return generateAlgorithmMap(); } // 保留这个方法用于特殊场景的动态注册(可选) void registerAlgorithm(String algorithmKey, TestAlgorithm algorithm) { state = {...state, algorithmKey: algorithm}; } }
其他替代思路
- 模块式注册:如果不想用代码生成,可以在每个算法文件里写一个
register函数,然后在应用启动时(比如main函数里)批量调用这些函数,给AlgorithmMap注册。这种方式比手动写映射表灵活,也没有反射开销,但需要手动维护调用列表。 - Riverpod初始化Provider:可以创建一个单独的
Provider来初始化所有算法,在build里实例化所有算法并构建映射表,本质是把注册逻辑移到了单独的Provider里,比直接在AlgorithmMap里写映射更清晰,但还是需要手动维护实例列表。
内容的提问来源于stack exchange,提问作者Dubl
相关产品推荐
相关产品推荐

