2015-12-22 17 views
5

Tôi quan tâm đến việc triển khai thực hiện mẫu thiết kế Service Locator linh hoạt trong Swift.Mẫu định vị dịch vụ trong Swift

Một cách tiếp cận ngây thơ có thể như sau:

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

Nhưng nó sẽ là nhiều tốt hơn nếu Locator Service sẽ có thể xử lý bất kỳ loại hình dịch vụ mà không thay đổi mã của nó. Làm thế nào điều này có thể đạt được trong Swift?

Lưu ý: Bộ định vị dịch vụ là mẫu thiết kế khá gây tranh cãi (thậm chí được gọi là chống mẫu), nhưng hãy tránh chủ đề này tại đây.

Trả lời

6

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ũ.

Các vấn đề liên quan