服务定位器模式在Swift中

我对Swift中灵活的通用Service Locatordevise模式实现感兴趣。

天真的做法可能如下:

// Services declaration protocol S1 { func f1() -> String } protocol S2 { func f2() -> String } // Service Locator declaration // Type-safe and completely rigid. protocol ServiceLocator { var s1: S1? { get } var s2: S2? { get } } final class NaiveServiceLocator: ServiceLocator { var s1: S1? var s2: S2? } // 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 = NaiveServiceLocator() sl.s1 = S1Impl() sl.s2 = S2Impl() return sl }() // Test run print(sl.s1?.f1() ?? "S1 NOT FOUND") // S1 OK print(sl.s2?.f2() ?? "S2 NOT FOUND") // S2 OK 

但是,如果服务定位器能够处理任何types的服务而不改变其代码,将会好得多。 Swift如何实现这一点?

注意 :服务定位器是一个相当有争议的devise模式(有时甚至称为反模式),但是请让我们在这里避免这个话题。

实际上,我们可以利用Swift的types推断能力来获得灵活的通用和types安全的服务定位器。 这是基本的实现 ( 要点 ):

 protocol ServiceLocator { func getService<T>() -> T? } final class BasicServiceLocator: ServiceLocator { // Service registry private lazy var reg: Dictionary<String, Any> = [:] private func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } func addService<T>(service: T) { let key = typeName(T) reg[key] = service //print("Service added: \(key) / \(typeName(service))") } func getService<T>() -> T? { let key = typeName(T) return reg[key] 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: S1? = sl.getService() let s2: S2? = sl.getService() print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK 

这已经是一个可用的实现,但是允许惰性服务初始化也是有用的。 更进一步,我们将有这个( 要点 ):

 protocol ServiceLocator { func getService<T>() -> T? } final class LazyServiceLocator: ServiceLocator { /// Registry record enum RegistryRec { case Instance(Any) case Recipe(() -> Any) func unwrap() -> Any { switch self { case .Instance(let instance): return instance case .Recipe(let recipe): return recipe() } } } /// Service registry private lazy var reg: Dictionary<String, RegistryRec> = [:] private func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } func addService<T>(recipe: () -> T) { let key = typeName(T) reg[key] = .Recipe(recipe) } func addService<T>(instance: T) { let key = typeName(T) reg[key] = .Instance(instance) //print("Service added: \(key) / \(typeName(instance))") } func getService<T>() -> T? { let key = typeName(T) var instance: T? = nil if let registryRec = reg[key] { instance = registryRec.unwrap() as? T // Replace the recipe with the produced instance if this is the case switch registryRec { case .Recipe: if let instance = instance { addService(instance) } default: break } } return instance } } 

它可以通过以下方式使用:

 // Services declaration protocol S1 { func f1() -> String } protocol S2 { func f2() -> String } // Services imlementation class S1Impl: S1 { let s2: S2 init(s2: S2) { self.s2 = s2 } func f1() -> String { return "S1 OK" } } class S2Impl: S2 { func f2() -> String { return "S2 OK" } } // Service Locator initialization let sl: ServiceLocator = { let sl = LazyServiceLocator() sl.addService { S1Impl(s2: sl.getService()!) as S1 } sl.addService { S2Impl() as S2 } return sl }() // Test run let s1: S1? = sl.getService() let s2: S2? = sl.getService() //let s2_: S2? = sl.getService() print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK 

漂亮整洁,不是吗? 我认为使用Service Locator和Dependency Injection可以避免前一种模式的缺点。