有没有更好的方法在Swift中进行依赖注入呢?

时间:2015-06-11 01:01:05

标签: swift generics design-patterns dependency-injection

swift的新手,我试图创建一个服务注册表:

class ServiceRegistry {

    static var instance = ServiceRegistry()

    private var registry = [String:AnyObject]()
    private init(){}

    func register<T>(key:T, value:AnyObject) {
        self.registry["\(T.self)"] = value
    }

    func get<T>(_:T) -> AnyObject? {
        return registry["\(T.self)"]
    }

}

但不是非常友好:

寄存器:

 ServiceRegistry.instance.register(CacheServiceProtocol.self, value:ImageCacheService())

提取:

if let cache = ServiceRegistry.instance.get(CacheServiceProtocol) as? CacheServiceProtocol { ... }

有更好的方法吗?摆脱as? CacheServiceProtocol

中的if let ...会很有用

3 个答案:

答案 0 :(得分:6)

Swinject 是Swift的依赖注入框架。在您的情况下,您可以在不使用as?的演员表的情况下使用它。

寄存器:

let container = Container()
container.register(CacheServiceProtocol.self) { _ in ImageCacheService() }

提取:

let cache = container.resolve(CacheServiceProtocol.self)!

此处cache被推断为CacheServiceProtocol类型。如果未注册指定的类型,resolve方法将返回nil。我们知道CacheServiceProtocol已经注册,因此使用!强制解包。

<强>更新

我没有完全回答这个问题。删除强制转换的实现是存储工厂闭包而不是registry中的值。这是一个例子。我还修改了key的类型。

class ServiceRegistry {
    static var instance = ServiceRegistry()

    private var registry = [String:Any]()
    private init(){}

    func register<T>(key:T.Type, factory: () -> T) {
        self.registry["\(T.self)"] = factory
    }

    func get<T>(_:T.Type) -> T? {
        let factory = registry["\(T.self)"] as? () -> T
        return factory.map { $0() }
    }
}

寄存器:

ServiceRegistry.instance.register(CacheServiceProtocol.self) {
    return ImageCacheService()
}

提取:

// The type of cache is CacheServiceProtocol? without a cast.
let cache = ServiceRegistry.instance.get(CacheServiceProtocol.self)

使用@autoclosure可能也不错。

答案 1 :(得分:1)

我看到您尝试实施服务定位器设计模式。它不是依赖注入本身,但这两种模式实际上可以相互补充。

我确实在Swift 2中实现了一个服务定位器,我对结果非常满意。请在此处查看我的代码:ServiceLocator.swift(可以使用)或BasicServiceLocator.swiftLazyServiceLocator.swift(以及使用示例)。

这是基本概念:

protocol ServiceLocator {

    func getService<T>(type: T.Type) -> T?
    func getService<T>() -> T?

}

extension ServiceLocator {

    func getService<T>() -> T? {
        return getService(T)
    }

}

func typeName(some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}

final class BasicServiceLocator: ServiceLocator {

    // Service registry
    private lazy var reg: Dictionary<String, Any> = [:]

    func addService<T>(instance: T) {
        let key = typeName(T)
        reg[key] = instance
        //print("Service added: \(key) / \(typeName(service))")
    }

    func getService<T>(type: T.Type) -> T? {
        return reg[typeName(T)] as? T
    }

}

示威:

// Services declaration

protocol S1 {
    func f1() -> String
}

protocol S2 {
    func f2() -> String
}

// Services imlementation

class S1Impl: S1 {
    func f1() -> String {
        return "S1 OK"
    }
}

class S2Impl: S2 {
    func f2() -> String {
        return "S2 OK"
    }
}

// Service Locator initialization

let sl: ServiceLocator = {
    let sl = BasicServiceLocator()
    sl.addService(S1Impl() as S1)
    sl.addService(S2Impl() as S2)
    return sl
}()

// Test run

let s1 = sl.getService(S1)
let s2: S2? = sl.getService(S2)

print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK
print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK

答案 2 :(得分:0)

正如其他海报所指出的那样,服务定位器模式实际上并不是DI。有些人甚至会说it's an anti-pattern

作为对您问题的一般回答 - 我相信一流的DI是实现上述目标的更好方法。我的建议是使用Typhoon但是有several other DI libs可用于Swift,例如Cleanse,看起来很有希望。