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 ...
会很有用
答案 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.swift和LazyServiceLocator.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,看起来很有希望。