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を取り出す。