Swift是驚人的,但還不成熟,所以有一些編譯器限制,其中包括通用協議。由於類型安全考慮,通用協議不能用作常規類型註釋。我在Hector Matos的帖子中發現了一個解決方法。 Generic Protocols & Their Shortcomings如何在swift中編寫多個刪除類型的模塊?
主要思想是使用類型擦除將泛型協議轉換爲泛型類,它很酷。但是在將這種技術應用於更復雜的場景時,我陷入了困境。
假設有一個抽象的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()
實現源和程序很簡單,因爲他們在的關係是不底:
class SourceImpl: Source {
func newData() -> Int { return 1 }
}
class ProcedureImpl: Procedure {
func process(data: Int) { print(data) }
}
PITA在實施Pipeline時出現
// 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())
}
}
呃,其實這個工程! 我在開玩笑嗎?我不滿意這一個,因爲PipelineImpl
的initializer
是相當通用的,所以我希望它在協議(我是這個癡迷錯誤?)。這導致兩端:
該協議
Pipeline
將是通用的。initializer
包含一個where子句,它指的是placeholder T
,所以我需要將placeholder T
作爲associated type
移動到協議中。然後該協議變成一個通用的,這意味着我不能直接在我的客戶端代碼中使用它 - 可能需要另一種類型的擦除。雖然我所能承受的麻煩寫另一類型擦除爲
Pipeline
協議的,我不知道該如何應對initializer function
因爲AnyPipeline<T>
類必須實現關於該協議的初始化,但它只是一個thunk class實際上,它不應該實現任何初始化器本身。保持協議
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
這個。
好一點,但也有可能存在着一些不同的前和後的工作在不同的'exec'函數來完成。不過,我想,@Hhish的方法仍然適用於這種情況。謝謝! –
@NandiinBao啊,我知道了 - 我編輯了我的答案來解釋這個。很高興能有所幫助:) – Hamish