DI Containerをサクッと実装してみる
AngularJSのInjectorのソースを読んでから一回DIライブラリを作って見たくなったので、何も考えずSwiftで実装してみる この方法が良い悪いの話は置いておいて、Angularのようなインターフェースを目指す。
class ApiClient {} class QiitaRepository { let client: ApiClient init(client: ApiClient) { self.client = client } }
let container = Container() .register(ApiClient.self) { ApiClient() } .register(QiitaRepository.self) { (client: ApiClient) in QiitaRepository(client: client) } container.resolve(QiitaRepository.self)
登録する値を一意に定めるためのKeyを作成する。 生成する(クラス|構造体)、それに必要なDependency、パラーメーターに応じて一意に定めればいい。
struct ServiceKey { let serviceType: Any.Type init(serviceType: Any.Type) { self.serviceType = serviceType print(serviceType) print(ObjectIdentifier(serviceType)) } } extension ServiceKey: Equatable { static func == (lhs: ServiceKey, rhs: ServiceKey) -> Bool { return lhs.serviceType == rhs.serviceType } } extension ServiceKey: Hashable { public func hash(into hasher: inout Hasher) { ObjectIdentifier(serviceType).hash(into: &hasher) } }
typealias Factory = () -> Any? final class Container { private var services: [ServiceKey: Factory] = [:] private func getService<Service>(_ type: Service.Type) -> Service? { let key = ServiceKey(serviceType: type) let service = services[key]?() return service as? Service } func register<Service>(_ type: Service.Type, value: @escaping () -> Service) { let key = ServiceKey(serviceType: type) services[key] = { value() } } func register<Service, Argument>(_ type: Service.Type, factory: @escaping (Argument) -> Service) { let key = ServiceKey(serviceType: type) services[key] = { guard let arg1 = self.getService(Argument.self) else { // argunemt1 is not registerd return nil } return factory(arg1) } } func resolve<Service>() -> Service { return getService(Service.self)! } }
DI
静的DI/動的DI
- container
- resolver
- injector
provider
inject
- register
- invoke
Object Pool
Containerにこのserviceをこのargumentとこのdepencdencyでお願いしますと登録する。 ContainerはAnyで型消去されたProviderを持つ resolveするタイミングで型情報を教えてProviderを取り出す。