Swift 引用自身和关联类型的协议

Swift Protocol referencing itself and associated type

我有一个责任链模式的经典实现,代码如下:

protocol Request {

    var firstName: String? { get }
    var lastName: String? { get }

    var email: String? { get }
    var password: String? { get }
    var repeatedPassword: String? { get }
} 

protocol Handler {

    var next: Handler? { get }

    func handle(_ request: Request) -> LocalizedError?
}

class BaseHandler: Handler {

    var next: Handler?

    init(with handler: Handler? = nil) {
        self.next = handler
    }

    func handle(_ request: Request) -> LocalizedError? {
        return next?.handle(request)
    }
}

所以我可以创建一个 PermissionHandler、LocationHandler、LoginHandler、一个 SignupHandler 并将它们组合成链。到目前为止一切顺利。

现在我想为其他目的创建一个责任链,假设一个 MediaContentPlayer CoR 具有不同类型的 MediaContentHandlers,我想使用泛型重构和重用基本代码。

所以我从Handler协议开始:

protocol Handler {

    associatedtype HandlerRequest
    var next: Handler? { get }

    func handle(_ request: HandlerRequest) -> LocalizedError?
}

但我收到错误 "Protocol 'Handler' can only be used as a generic constraint because it has Self or associated type requirements"。

有没有办法在使用 associatedtype 时在协议本身内部引用协议?或者用另一种方式让上面的代码不依赖于特定的类型?

我会使用以下内容

protocol Handler {

    associatedtype HandlerRequest
    associatedtype NextHandler: Handler

    var next: Self.NextHandler? { get }

    func handle(_ request: HandlerRequest) -> LocalizedError?
}

你会照顾这样的东西:

protocol Handler {
    // ...
    var next: some Handler<HandlerRequest == Self.HandlerRequest>?  { get }
    // ...
}

这里的问题是 Swift 不(还)支持不透明的 return 类型,这些类型是具有关联类型的协议。

解决此限制的方法是为 next 属性:

使用类型橡皮擦
protocol Handler {
    associatedtype HandlerRequest

    // shift the generic from a protocol with associated type to a generic struct
    var next: AnyHandler<HandlerRequest>? { get }

    func handle(_ request: HandlerRequest) -> LocalizedError?
}

struct AnyHandler<HandlerRequest> {

    private var _handle: (HandlerRequest) -> LocalizedError?
    private var _next: () -> AnyHandler<HandlerRequest>?

    init<H: Handler>(_ handler: H) where H.HandlerRequest == HandlerRequest {
        _next = { handler.next }
        _handle = handler.handle
    }
}

extension AnyHandler: Handler {

    var next: AnyHandler<HandlerRequest>? { return _next() }

    func handle(_ request: HandlerRequest) -> LocalizedError? {
        return _handle(request)
    }
}

这样您既可以从协议中获益,又可以将 next 属性 绑定到您需要的处理程序请求类型。

作为使用协议的额外好处,您仍然可以从基础 class:

中受益于默认实现
extension Handler {
    func handle(_ request: HandlerRequest) -> LocalizedError? {
        return next?.handle(request)
    }
}

这就是 Swift 中协议的酷炫之处,它们允许您通过改进多态性的概念来避免 classes 并尽可能多地使用值类型。


用法示例:

struct LoginHandler: Handler {
    var next: AnyHandler<AccountRequest>?

    func handle(_ request: AccountRequest) -> LocalizedError? {
        // do the login validation
    }
}

struct SignupHandler: Handler {
    var next: AnyHandler<AccountRequest>?

    func handle(_ request: AccountRequest) -> LocalizedError? {
        // do the signup validation
    }
}

extension Handler {
    // Helper function to easily create a type erased AnyHandler instance    
    func erase() -> AnyHandler<HandlerRequest> {
        return AnyHandler(self)
    }
}

// now let's put the handers to work:
let loginHandler = LoginHandler()
let signupHandler = SignupHandler(next: loginHandler.erase())
let someOtherAccountHandler = SomeOtherAccountHandler(next: signupHandler.erase())