2015-12-22 7 views
5

में सेवा लोकेटर पैटर्न मैं स्विफ्ट में एक लचीला सार्वभौमिक सेवा लोकेटर डिजाइन पैटर्न कार्यान्वयन में रूचि रखता हूं।स्विफ्ट

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

लेकिन यह ज्यादा बेहतर होगा कि सेवा लोकेटर अपने कोड को बदले बिना सेवा के किसी भी प्रकार को हैंडल करने में सक्षम हो जाएगा:

एक अनुभवहीन दृष्टिकोण इस प्रकार हो सकता है। स्विफ्ट में यह कैसे हासिल किया जा सकता है?

नोट: सेवा लोकेटर एक बहुत विवादास्पद डिजाइन पैटर्न (यहां तक ​​कि एक विरोधी पैटर्न कभी कभी कहा जाता है) है, लेकिन इस विषय के यहां से बचने बताएं।

उत्तर

6

असल में, हम एक लचीला सार्वभौमिक और प्रकार-सुरक्षित सेवा लोकेटर पाने के लिए स्विफ्ट के प्रकार निष्कर्ष क्षमताओं का दोहन कर सकते हैं।

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 

सुंदर स्वच्छ, है ना? और मुझे लगता है कि निर्भरता इंजेक्शन के संयोजन के साथ एक सेवा लोकेटर का उपयोग करने से पूर्व पैटर्न के कुछ विपक्ष से बचने की सुविधा मिलती है।