Thực ra, chúng ta có thể khai thác khả năng suy luận kiểu Swift để có được một bộ định vị dịch vụ và loại an toàn. Dưới đây là việc thực hiện cơ bản (gist):
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
}
}
Sau đó nó có thể được sử dụng như sau:
// 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
này đã là một thực thể sử dụng, nhưng nó sẽ là cũng hữu ích cho phép lười biếng khởi tạo dịch vụ. Đi thêm một bước xa hơn, chúng tôi sẽ có điều này (gist):
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
}
}
Nó có thể được sử dụng theo cách sau:
// 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
Khá gọn gàng, không phải là nó? Và tôi nghĩ rằng sử dụng một Service Locator kết hợp với Dependency Injection cho phép tránh một số khuyết điểm của mẫu cũ.