服务定位器模式在Swift [英] Service Locator pattern in Swift

查看:149
本文介绍了服务定位器模式在Swift的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Swift中灵活的通用服务定位器设计模式实现感兴趣。

I'm interested in a flexible universal Service Locator design pattern implementation in Swift.

一个天真的方法可能如下:

A naive approach may be as follows:

// 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中如何实现?

But it would be much better if the Service Locator will be able to handle any type of service without changing its code. How this can be achieved in Swift?

注意:服务定位器是一个非常有争议的设计模式(甚至被称为

Note: the Service Locator is a pretty controversial design pattern (even called an anti-pattern sometimes), but please let's avoid this topic here.

推荐答案

实际上,我们可以利用Swift的类型推理能力获得灵活的通用型和安全型型的服务定位器。这是基本实现要点):

Actually, we can exploit Swift's type inference abilities to get a flexible universal and type-safe Service Locator. Here is the basic implementation (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
    }

}

然后可以使用如下:

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

这已经是一个可用的实现,但它也是有用的允许延迟服务初始化。进一步我们将会有(要点):

This is already a usable implementation, but it would be also useful to allow lazy services initialization. Going one step further we'll have this (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
    }

}

可以通过以下方式使用:

It can be used in the following way:

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

很整洁,不是吗?我认为使用服务定位器与依赖注入结合使用可以避免前一种模式的一些缺点。

Pretty neat, isn't it? And I think that using a Service Locator in conjunction with Dependency Injection lets avoid some cons of the former pattern.

这篇关于服务定位器模式在Swift的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆