असल में, हम एक लचीला सार्वभौमिक और प्रकार-सुरक्षित सेवा लोकेटर पाने के लिए स्विफ्ट के प्रकार निष्कर्ष क्षमताओं का दोहन कर सकते हैं।
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
}
}
यह तो इस प्रकार के रूप में इस्तेमाल किया जा सकता है:: यहाँ बुनियादी कार्यान्वयन (gist) है
// 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
यह पहले से ही एक प्रयोग करने योग्य कार्यान्वयन है, लेकिन यह भी आलसी अनुमति देने के लिए उपयोगी होगा सेवाएं प्रारंभिकरण। एक कदम आगे जा रहे हैं हम होगा यह (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
}
}
यह निम्नलिखित तरीके से इस्तेमाल किया जा सकता:
// 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
सुंदर स्वच्छ, है ना? और मुझे लगता है कि निर्भरता इंजेक्शन के संयोजन के साथ एक सेवा लोकेटर का उपयोग करने से पूर्व पैटर्न के कुछ विपक्ष से बचने की सुविधा मिलती है।