Dart-Simple-DI

这是什么

需求描述

需求就是为了项目间解耦, 需要一个简单的DI工具
目标是可以直接通过代码直接能提示出目前已经注册好的类

当前情况

目前已有的方案是直接用一个类常驻内存作为一个中转站
使用效果为

1
2
3
4
5
6
7
8
9
// 注册一个名为[VideoRewardWrapper]的类
SharedStateStore().set<VideoRewardWrapper>(
PredefinedKeys.videoRewardManager, VideoRewardManager());

// 使用的时候指定具体的类型和key来获取之前在内存中注册的类
SharedStateStore().get<VideoRewardWrapper>(
PredefinedKeys.videoRewardManager)).ifPresent((v) {
/* using it do something */
});

这个方案最大的问题是需要手敲具体的类和key, 从一个黑盒里面拿东西感觉总是不太好

期望的形态

注册成功的依赖都直接是Provider的成员, 现在的形式如果不是自己写的话很可能就不知道是否有这个东西

1
DependenciesProvider.

调查的一些方案

Injector, 使用起来效果和SharedStateStore效果差不多, 只是这个的key是可选参数, 这个方案的好处就是简单, 但是因为和已有方案基本一致, 再引入这个依赖意思不大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Use this static instance
final injector = Injector.appInstance;
// Register a dependency
injector.registerDependency<Engine>(() => Engine());

injector.registerDependency<Car>(() {
final engine = injector.get<Engine>();
return CarImpl(engine: engine);
});

//Use custom Factories by extending [Factory]
injector.register(CustomFactory(() => Engine()));

// Maybe you want to register a class and you need it as a singleton
injector.registerSingleton<Database>(() => TikkrDatabase());

// Now you can easily get your dependencies / singletons with one line
database = injector.get<Database>();
customerCar = injector.get<Car>();

Qinject可以达到需求的目标, 它通过annotation来进行必要的标记说明, 然后通过builder runner根据之前的指示生成具体的代码. 这个库比较高级, 好多参数都能通过标记来解决, 有种Spring DI的感觉.
可是每次改代码都得重新生成一下代码, 感觉又没必要这么重. 同时这种方案如何处理潜在的循环依赖/依赖初始化顺序问题也没细看. 这个方案备选

最终选定的方案

基于之前调研的内容, 最后决定直接沿用SharedStateStore, 然后加extension. 这样既没有增加很多复杂度, 又达成了需求的目标; 但弊端是声明与实现分离了, 同时也没有隐藏填充的细节(Qinject是生成的所以也算隐藏了细节), 这就是这个方案所必须承受的代价了.

1
2
3
4
5
6
7
8
extension Privider on SharedStateStore {
// 只要在这边明确声明可获取的类型就好了
AdManager? get adManager {
// 尴尬的地方就是需要在别处set, 如果也聚合在这里的话就又比较臃肿
final m = get<Admanager>('ad_manager_key').orNull;
return m;
}
}

部分实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/// SharedStateStore.dart
import 'dart:async';
import 'dart:collection';
import 'package:quiver/core.dart';
import 'package:tuple/tuple.dart';

class SharedStateStore {
static final SharedStateStore _singleton = SharedStateStore._internal();
final Map<String, dynamic> _store = <String, dynamic>{};
late StreamController<Tuple2<String, dynamic>> _streamController;

factory SharedStateStore() {
return _singleton;
}

SharedStateStore._internal() {
_streamController = StreamController.broadcast(sync: false);
}

void set<T>(String key, T value, {bool withNotify = true}) {
_store[key] = value;
if (withNotify) {
_streamController.add(Tuple2<String, dynamic>(key, value));
}
}

Optional<T> get<T>(String key) {
final obj = _store[key];
if (obj == null || obj is T) {
return Optional.fromNullable(_store[key]);
}
throw TypeError();
}

void invalid(String key) {
_store.remove(key);
_streamController.add(Tuple2<String, dynamic>(key, null));
}

void clear() {
_store.clear();
}

Stream<Optional<T>> onKeyChange<T>(String key) {
return _streamController.stream
.where((e) => e.item1 == key)
.map((e) => Optional.fromNullable(e.item2));
}

Map<String, dynamic> get store => UnmodifiableMapView(_store);
}

此模块已经发布在pub