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())
我有一个责任链模式的经典实现,代码如下:
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())