首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Swift Mediator模式-通用协议集合

Swift Mediator模式-通用协议集合
EN

Stack Overflow用户
提问于 2019-01-03 20:14:28
回答 1查看 252关注 0票数 0

我正在尝试在Swift中实现一个通用的Mediator pattern,并具有以下协议和类:

代码语言:javascript
复制
protocol Request {
}

protocol Handler {
    associatedtype TRequest = Request

    func handle(_ request: TRequest)
}

class RequestProcessor {

    func register<THandler: Handler>(_ handler: THandler) {

    }

    func handle(_ request: Request) {

    }

}

其预期用法为(例如):

代码语言:javascript
复制
struct LoginRequest: Request {
    let username: String
    let password: String
}

struct LogoutRequest: Request {
    let userId: Int
}

class LoginHandler: Handler {
    func handle(_ request: LoginRequest) {
        // do something
    }
}

class LogoutHandler: Handler {
    func handle(_ request: LogoutRequest) {
        // do something
    }
}

// Setup the processor and register handlers

let processor = RequestProcessor()
processor.register(LoginHandler())
processor.register(LogoutHandler())

// The processor handles any kind of Request, in this case a LoginRequest

processor.handle(LoginRequest(username: "steve", password: "..."))

// The LoginHandler's handle method will be called

但是,我不确定如何存储Handler对象的集合,因为它是一个具有关联类型的协议。我知道类型擦除,我已经阅读了这里的几个答案以及关于这个主题的各种文章(12),但我不确定如何将其应用于我的情况。

EN

回答 1

Stack Overflow用户

发布于 2019-01-03 22:30:28

首先,标准建议:

我正在尝试在Swift中实现一个通用的中介器模式

不要这样做。从你试图解决的实际问题开始,为这个问题设计好的和必要的抽象。不要仅仅为了通用而创建通用的东西。斯威夫特会一次又一次地咬你。即使是stdlib,它确实需要超泛型的东西,也经常不得不走出纯Swift来完成它(使用编译器专业知识和gyb模板)。“通用性”本身并不是一个目标。几乎可以肯定的是,你把事情搞得太复杂了。每个人都是这样。

好了,这不是问题所在。第二条建议:这不是使用具有关联类型(PAT)的协议的好方法。PAT的要点是将方法添加到类型,而不是类型。您永远不会传递Collection本身或存储该“类型”的内容。您创建的方法通常可以处理任何类型的Collection。没有像[Collection]这样的类型。

您的方法的根本问题是,如果不使用as?强制转换,就无法实现RequestProcessor.process(),这打破了类型安全的原则。processor是如何知道调用LoginHandler.process的?为什么是那个?如果两个不同的处理程序接受LoginRequest怎么办?如果没有处理程序接受该类型怎么办?

您在这里设计的不是Mediator模式。Mediator模式将共享单个接口的同事联合在一起,因此它将如下所示:

代码语言:javascript
复制
class RequestProcessor<Request> {

    var handlers: [(Request) -> Void] = []
    func register(handler: @escaping (Request) -> Void) {
        handlers.append(handler)
    }

    func handle(request: Request) {
        for handler in handlers {
            handler(request)
        }
    }
}

而且对于每种类型的请求都有一个RequestProcessor,而不是一个通用的“每种请求的处理器”。创建一个泛型NotificationCenter (在Swift中)必然会消除类型安全,在这种情况下,您基本上是在创建一个略带Swiftier的a。(可以创建一个类型安全的版本,但它需要依赖类型,这是Swift没有的一个非常复杂的类型特性。)

好吧,那么也许你真的想要这个中央集线器,谁需要类型安全呢?为什么不行?您只需说明您的意思,即任何处理程序都必须能够接受任何请求,即使它不执行操作。编译器不能证明比这更具体的东西了,因为在编译时它不知道类型。所以,好吧,as?它死了。

代码语言:javascript
复制
protocol Request {}

protocol Handler {
    func canHandle(_ request: Request) -> Bool
    func handle(_ request: Request)
}

class RequestProcessor {

    private var handlers: [Handler] = []

    func register(_ handler: Handler) {
        handlers.append(handler)
    }

    func handle(_ request: Request) {
        for handler in handlers where handler.canHandle(request) {
            handler.handle(request)
        }
    }
}

class LoginHandler: Handler {
    func canHandle(_ request: Request) -> Bool {
        return request is LoginRequest
    }

    func handle(_ request: Request) {
        guard let loginRequest = request as? LoginRequest else { return }
        // handle loginRequest
    }
}

但我几乎肯定会摆脱Mediator模式。如果目标是换入换出处理器来进行测试或诸如此类的事情,我只会使用典型的依赖注入技术。将LoginHandler传递给创建LoginRequest的任何方法。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54022102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档