Swift 很了不起,但还不成熟,所以有一些编译器限制,其中包括泛型协议。由于类型安全考虑,泛型协议不能用作常规类型注释。我在 Hector Matos 的帖子中找到了解决方法。通用协议及其缺点
主要思想是使用类型擦除将泛型协议转换为泛型类,这很酷。但是在将这项技术应用于更复杂的场景时,我陷入了困境。
假设有一个抽象的Source,它产生数据,一个抽象的Procedure,它处理该数据,还有一个管道,它结合了一个源和一个数据类型匹配的过程。
protocol Source {
associatedtype DataType
func newData() -> DataType
}
protocol Procedure {
associatedtype DataType
func process(data: DataType)
}
protocol Pipeline {
func exec() // The execution may differ
}
我希望客户端代码简单如下:
class Client {
private let pipeline: Pipeline
init(pipeline: Pipeline) {
self.pipeline = pipeline
}
func work() {
pipeline.exec()
}
}
// Assume there are two implementation of Source and Procedure,
// SourceImpl and ProcedureImpl, whose DataType are identical.
// And also an implementation of Pipeline -- PipelineImpl
Client(pipeline: PipelineImpl(source: SourceImpl(), procedure: ProcedureImpl())).work()
实现 Source 和 Procedure 很简单,因为它们位于依赖关系的底部:
class SourceImpl: Source {
func newData() -> Int { return 1 }
}
class ProcedureImpl: Procedure {
func process(data: Int) { print(data) }
}
执行 Pipeline 时出现 PITA
// A concrete Pipeline need to store the Source and Procedure, and they're generic protocols, so a type erasure is needed
class AnySource<T>: Source {
private let _newData: () -> T
required init<S: Source>(_ source: S) where S.DataType == T {
_newData = source.newData
}
func newData() -> T { return _newData() }
}
class AnyProcedure<T>: Procedure {
// Similar to above.
}
class PipelineImpl<T>: Pipeline {
private let source: AnySource<T>
private let procedure: AnySource<T>
required init<S: Source, P: Procedure>(source: S, procedure: P) where S.DataType == T, P.DataType == T {
self.source = AnySource(source)
self.procedure = AnyProcedure(procedure)
}
func exec() {
procedure.process(data: source.newData())
}
}
呃,其实这个有效!我在开玩笑吗?不。
我对这个不满意,因为initializer
ofPipelineImpl
非常通用,所以我希望它出现在协议中(我对这种痴迷有错吗?)。这导致了两个目的:
该协议
Pipeline
将是通用的。initializer
包含一个 where 子句引用placeholder T
,所以我需要将intoplaceholder T
协议作为associated type
. 然后协议变成一个通用协议,这意味着我不能直接在我的客户端代码中使用它——可能需要另一种类型擦除。虽然我可以承受为协议编写另一种类型擦除的麻烦
Pipeline
,但我不知道如何处理,initializer function
因为AnyPipeline<T>
该类必须实现与协议有关的初始化程序,但实际上它只是一个thunk 类,不应该实现任何初始化器本身。保持协议
Pipeline
非通用。用写之initializer
类的init<S: Source, P: Procedure>(source: S, procedure: P) where S.DataType == P.DataType
我可以防止协议是通用的。这意味着该协议仅声明“源和过程必须具有相同的数据类型,我不在乎它是什么”。这更有意义,但我未能实现确认此协议的具体类
class PipelineImpl<T>: Protocol { private let source: AnySource<T> private let procedure: AnyProcedure<T> init<S: Source, P: Procedure>(source: S, procedure: P) where S.DataType == P.DataType { self.source = AnySource(source) // doesn't compile, because S is nothing to do with T self.procedure = AnyProcedure(procedure) // doesn't compile as well } // If I add S.DataType == T, P.DataType == T condition to where clasue, // the initializer won't confirm to the protocol and the compiler will complain as well }
那么,我该如何处理呢?
感谢您阅读allll
本文。