[英]Two protocols with associated types: How to link their implementations in a type-safe way?
在為我的應用程序創建網絡層時,我通常使用通用方法從服務器獲取數據,例如
func fetch<R: Resource>(_ resource: R, completion: ((Result<R.Model, Error>) -> Void))
我發現這非常優雅,因為我只需要實現一次我的 fetch 方法,但仍然使用此協議獲得類型安全:
protocol Resource {
associatedType Model
var endPointPath: String { get }
}
這樣,我可以輕松地在任何地方添加新資源(端點),而無需修改現有代碼,並且包含 API 端點的資源始終通過關聯類型直接與結果的Model
類型掛鈎——我總是知道在獲取給定Resource
。
我可以簡單地獲取這個資源:
struct ArticlesResource: Resource {
typealias Model = [Article]
let endPointPath = "/articles"
}
通過此調用並返回類型安全的 model :
myAPI.fetch(ArticleResource()) { result in
switch result {
case let .success(model):
// model is of type [Article]
case .failure:
// Request failed...
}
}
這種方法一直很適合我——直到現在。
現在我正在處理一個給定的網絡框架,它使用與我相同的方法(驚訝:)。 它還使用協議來定義請求,並且該協議還具有網絡調用結果的關聯類型
我需要包裝這個具體的網絡框架,這樣它就不會暴露在我自己的網絡層之外(因為它是我的網絡層的使用者不應該關心的實現細節)。
所以讓我們假設,這個第 3 方框架具有與我自己的網絡層相同的 API(只是具有不同的 model 和資源類型):兩者都有一個在與各自關聯的(特定於域的)資源類型中通用的fetch
方法model 類型(通過關聯類型,見圖)。
struct MyAPI {
private let networkingClient: ThirdPartyNetworkingClient
func fetch<R: Resource>(_ resource: R, completion: ((Result<R.Model, Error>) -> Void)) {
// 1. Map my own Resource to ThirdPartyResource
let networkResource = ? 💥
// 2. Fetch ThirdPartyResource
networkingClient.fetch(networkResource) { result in
// 3. Map ThirdPartyModel to my own Model
let model = R.Model(...) 💥
// 4. Return my own Model to the app via completion handler
completion(.success(model))
}
}
}
(在上面的示例中,為了簡潔起見,我假設了愉快的路徑並忽略了任何錯誤。)
在第 1 步中,我遇到了一個問題:我在一個通用的 function ThirdPartyResourceType
中,所以我需要一個方法來將我自己的資源類型R
以通用方式 ResourceType 映射到第三部分。
func mapToThirdPartyResource<R: Resource>(_ myResource: R) -> ThirdPartyResource {
// ... map ...
}
但是, ThirdPartyResourceType
具有關聯的類型要求,因此任何具有此類簽名的 function 都不會編譯。 你們都知道這個臭名昭著的 Swift 編譯器錯誤:
Protocol 'ThirdPartyResourceType' can only be used as a generic constraint because it has Self or associated type requirements
如果不指定協議的關聯類型,則類型信息不完整,因此編譯器無法使用它(僅作為通用約束)。 因此,不可能有通用的 function 將任何給定的 Resource 映射到其各自的ThirdPartyResourceType
。
同樣,在第 3 步中,出於同樣的原因,我無法將 map 從第 3 方庫的 model 類型返回到我自己的 Model 類型。
雖然在我看來這是 Swift 的語言限制,它只是阻止我這樣做,但我並不完全確定我是否足夠清楚地看到所有替代方案。 有沒有辦法在不失去類型安全的情況下實現所描述的網絡層抽象?
從邏輯上講,整個鏈條是緊密相連的:
Resource → ThirdPartyResource
↓
Model ← ThirdPartyModel
我正在尋找一種在ThirdPartyResorce
中完全像這樣連接它的方法,以通用方式連接所有這些類型 - 而不會將第三方資源和-Model
暴露給外界(消費者應用程序)。 那么有沒有辦法繞過這個限制呢?
為了讓事情更具體一點,這里有兩個示例協議:
protocol Resource {
associatedtype Model
}
protocol ThirdPartyResource {
associatedtype Model
var query: String { get }
}
第 3 方網絡庫可能有一個網絡客戶端,它公開以下通用fetch
function:
struct ThirdPartyNetworkingClient {
func fetch<R: ThirdPartyResource>(_ resouce: R, completion: (Result<R.Model, Error>) -> ()) {
// ...
}
}
function 是一個實現細節,不應向客戶端應用程序公開。 相反,我的網絡層公開了它自己的通用fetch
function,它在我的Resource
協議而不是ThirdPartyResource
上運行:
func fetch<R: Resource>(_ resource: R, completion: (Result<R.Model, Error>) -> ()) {
let thirdPartyResource = thirdPartyResource(for: resource) // 1️⃣ Mapping
thridPartyNetworkingClient.fetch(thirdPartyResource) { result in
switch result {
case .success(let thirdPartyModel):
let model = // 2️⃣ map ThirdPartyResource.Model to our Resource.Model and return to client
completion(.success(model))
break
case .failure:
// handle error
break
}
}
}
我正在尋找的通用映射函數(這個問題的全部內容)分別用 1️⃣ 和 2️⃣ 標記。 I need a point to convert a Resource
to the respective ThirdPartyResource
in order to perform the request with the 3rd party networking client, and when it returns a result, I need a way to map the ThirdPartyResource.Model
back to my own layer's model ( Resource.Model
)。
希望這能讓事情更清楚。
我不確定我是否遺漏了什么,但我認為步驟 1 中的問題可能可以這樣解決:
struct AnyThirdPartyResource<M>: ThirdPartyResource {
typealias Model = M
var query: String
init(_ query: String) {
self.query = query
}
}
func transform<R: Resource>(_ resource: R) -> AnyThirdPartyResource<R.Model> {
return AnyThirdPartyResource<R.Model>(resource.query)
}
AnyThirdPartyResource
類型不必暴露給客戶端,僅用於獲取 function。
In regards to problem 2, I think if the two Model types are not the same, you'd have to specify a transform function from ThirdPartyResource.Model -> Resource.Model. 也許這可能是您框架的相應資源上的內部 function ?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.