我正在尝试在Swift中实现一个通用的Mediator pattern,并具有以下协议和类:
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对象的集合,因为它是一个具有关联类型的协议。我知道类型擦除,我已经阅读了这里的几个答案以及关于这个主题的各种文章(1,2),但我不确定如何将其应用于我的情况。
发布于 2019-01-03 22:30:28
首先,标准建议:
我正在尝试在Swift中实现一个通用的中介器模式
不要这样做。从你试图解决的实际问题开始,为这个问题设计好的和必要的抽象。不要仅仅为了通用而创建通用的东西。斯威夫特会一次又一次地咬你。即使是stdlib,它确实需要超泛型的东西,也经常不得不走出纯Swift来完成它(使用编译器专业知识和gyb模板)。“通用性”本身并不是一个目标。几乎可以肯定的是,你把事情搞得太复杂了。每个人都是这样。
好了,这不是问题所在。第二条建议:这不是使用具有关联类型(PAT)的协议的好方法。PAT的要点是将方法添加到类型,而不是类型。您永远不会传递Collection本身或存储该“类型”的内容。您创建的方法通常可以处理任何类型的Collection。没有像[Collection]这样的类型。
您的方法的根本问题是,如果不使用as?强制转换,就无法实现RequestProcessor.process(),这打破了类型安全的原则。processor是如何知道调用LoginHandler.process的?为什么是那个?如果两个不同的处理程序接受LoginRequest怎么办?如果没有处理程序接受该类型怎么办?
您在这里设计的不是Mediator模式。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,而不是一个通用的“每种请求的处理器”。创建一个泛型NotificationCenter (在Swift中)必然会消除类型安全,在这种情况下,您基本上是在创建一个略带Swiftier的a。(可以创建一个类型安全的版本,但它需要依赖类型,这是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模式。如果目标是换入换出处理器来进行测试或诸如此类的事情,我只会使用典型的依赖注入技术。将LoginHandler传递给创建LoginRequest的任何方法。
https://stackoverflow.com/questions/54022102
复制相似问题