2015-06-11 11 views
6

Neu schnell, ich habe versucht, ein Service-Registry zu erstellen:Gibt es eine bessere Möglichkeit, die Abhängigkeitsinjektion in Swift durchzuführen?

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)"] 
    } 

} 

aber nicht super freundlich:

Register:

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

Abrufen:

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

Irgendein besserer Weg? Es wäre nützlich, um loszuwerden, die as? CacheServiceProtocol im if let ...

Antwort

6

Swinject ist ein Dependency Injection-Framework für Swift. In Ihrem Fall können Sie es ohne die Besetzung mit as? verwenden.

Register:

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

Abrufen:

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

Hier cache als CacheServiceProtocol Typ abgeleitet wird. Die resolve-Methode gibt nil zurück, wenn der angegebene Typ nicht registriert ist. Wir wissen, CacheServiceProtocol ist bereits registriert, so dass die Force-Unwrap mit ! verwendet wird.

UPDATE

ich auf die Frage nicht genau beantworten. Eine Implementierung zum Entfernen der Besetzung speichert Fabrikschließungen anstelle von Werten in der registry. Hier ist das Beispiel. Ich habe auch den Typ key geändert.

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() } 
    } 
} 

Register:

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

Abrufen:

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

@autoclosure verwenden, kann es auch gut sein.

+0

Dieser Blogbeitrag über Swinject könnte auch hilfreich sein: https://yoichitgy.github.io/post/dependency-injection-framework-for-swift-introduction-to-swinject/ –

1

Ich sehe dein Versuch Service Locator Design-Muster zu implementieren. Es ist nicht Dependency Injection selbst, aber diese beiden Muster können sich tatsächlich ergänzen.

Ich habe auch einen Service Locator in Swift 2 implementiert und ich bin ziemlich glücklich mit dem Ergebnis. Werfen Sie einen Blick auf meinen Code hier: ServiceLocator.swift (sofort einsatzbereit) oder BasicServiceLocator.swift und LazyServiceLocator.swift (mit Anwendungsbeispielen).Hier

ist das Grundkonzept:

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 
    } 

} 

und Demonstration:

// 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 
0

Als einer der anderen Plakate wies darauf hin, das Service Locator Muster ist nicht wirklich DI. Manche würden sogar so weit gehen zu sagen it's an anti-pattern.

Als allgemeine Antwort auf Ihre Frage - ich glaube, erstklassige DI ist ein besserer Weg, um das oben genannte zu erreichen. Mein Vorschlag wäre, Typhoon zu verwenden, aber es gibt several other DI libs für Swift wie Cleanse, das sehr vielversprechend aussieht.

Verwandte Themen