我正在尝试实现一个通用的 Mediator pattern在 Swift 中并具有以下协议(protocol)和类:
protocol Request {
}
protocol Handler {
associatedtype TRequest = Request
func handle(_ request: TRequest)
}
class RequestProcessor {
func register<THandler: Handler>(_ handler: THandler) {
}
func handle(_ request: Request) {
}
}
预期用途是(例如):
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
对象的集合,因为它是一个具有关联类型的协议(protocol)。我知道类型删除并在这里阅读了几个答案以及关于该主题的各种文章( 1 , 2 )但我不确定如何将它应用到我的情况。
最佳答案
首先,标准建议:
I am trying to implement a general-purpose Mediator pattern in Swift
不要。从您尝试解决的实际问题开始,并为该问题设计良好且必要 的抽象。不要为了通用而创建通用的东西。 Swift 会一遍又一遍地咬你。即使是 stdlib,它确实需要 super 泛型的东西,也常常不得不跳出纯 Swift 的范围来实现它(使用编译器专业知识和 gyb 模板)。 “通用”本身并不是目标。您几乎可以肯定这太复杂了。每个人都这样。
好吧,那不碍事了。第二条建议:这不是使用关联类型协议(protocol) (PAT) 的好方法。 PAT 的要点是向类型添加方法,而不是成为 类型。您永远不会传递 Collection
本身或存储该“类型”的内容。您创建的方法通常适用于任何 Collection
类型。没有 [Collection]
这样的类型。
您的方法的根本问题是,如果不求助于 as?
转换,就无法实现 RequestProcessor.process()
,这破坏了类型安全点。 processor
如何知道调用 LoginHandler.process
?为什么是那个?如果两个不同的处理程序接受了 LoginRequest
会怎样?如果没有处理程序接受该类型怎么办?
您在这里设计的不是调解器模式。 Mediator 模式将共享一个界面的同事联合在一起,因此它看起来像这样:
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
,而不是一个通用的“每种请求的处理器”。创建一个通用的(在 Swift 中)必然会消除类型安全,在这种情况下,您基本上是在创建一个稍微更快速的 NotificationCenter
。 (可以创建一个类型安全的版本,但它需要依赖类型,这是 Swift 所没有的非常复杂的类型特性。)
好吧,也许你真的想要这个中央集线器,谁需要类型安全?为什么不?您只需要说出您的意思,即任何处理程序都必须能够接受任何请求,即使它不对请求采取行动。编译器无法证明比这更具体的东西,因为在编译时它不知道类型。太好了,as?
死了。
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 模式。如果目标是换入和换出处理器以进行测试或诸如此类的事情,我只会使用典型的依赖注入(inject)技术。将 LoginHandler
传递给创建 LoginRequest
的任何方法。
关于Swift Mediator Pattern - 通用协议(protocol)的集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54022102/