我对Swift中灵活的通用服务定位器设计模式实现感兴趣.
一种天真的方法可能如下:
// 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
但是,这将是很多更好,如果服务定位器将能够处理任何类型的服务,而不改变其代码.如何在Swift中实现这一目标?
注意:服务定位器是一个非常有争议的设计模式(有时甚至称为反模式),但请让我们在这里避免这个主题.
实际上,我们可以利用Swift的类型推断功能来获得灵活的通用和类型安全的服务定位器.这是基本的实现(要点):
protocol ServiceLocator { func getService() -> T? } final class BasicServiceLocator: ServiceLocator { // Service registry private lazy var reg: Dictionary = [:] private func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } func addService (service: T) { let key = typeName(T) reg[key] = service //print("Service added: \(key) / \(typeName(service))") } func getService () -> 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? } 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 = [:] private func typeName(some: Any) -> String { return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)" } func addService (recipe: () -> T) { let key = typeName(T) reg[key] = .Recipe(recipe) } func addService (instance: T) { let key = typeName(T) reg[key] = .Instance(instance) //print("Service added: \(key) / \(typeName(instance))") } func getService () -> 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
很整洁,不是吗?我认为将服务定位器与依赖注入结合使用可以避免使用前一种模式.