![](/img/trans.png)
[英]Associated type constraints with generic types and protocols enables bypass of the WHERE clause
[英]How to implement generic base classes that conform to protocols having type constraints and implement a factory for those generic types?
我试图在我的项目中实现存储库模式。 我希望设计足够灵活,以便将来需要时可以换掉底层实现。 例如,我希望能够支持 Realm,但如果需要,可以将其与 Core Data 交换。 我还想实施假货进行测试。
我正在用假货开始实现,我想做的是实现一个 FakeRepository 基础 class 具有符合存储库协议的通用约束。
请注意,我省略了一些实现细节以使帖子尽可能简短。
protocol Repository {
associatedtype Item
var count: Int { get }
func item(at index: Int) -> Item
}
class FakeRepository<Model>: Repository where Model: Identifiable {
private var items: [Model] = []
var count: Int { items.count }
func item(at index: Int) -> Model {
return items[index]
}
}
我还想为我打算支持的每个模型定义一个协议。 例如,用户存储库。
struct User: Identifiable {
let id: Int
let name: String
}
protocol UserRepository: Repository where Item == User { }
现在我所要做的就是定义一个具体的 FakeUserRepository 并且我不需要实现任何东西,因为所有的工作都已经完成了。
class FakeUserRepository: FakeRepository<User>, UserRepository { }
我遇到的麻烦是当我开始实现工厂模式时。 我想做的是这样的事情,但是工厂协议无法编译,因为 UserRepository 具有相关的类型要求。
protocol UserRepositoryFactory {
func makeRepository() -> UserRepository // DOES NOT COMPILE
}
struct FakeUserRepositoryFactory: UserRepositoryFactory {
func makeRepository() -> UserRepository {
return FakeUserRepository()
}
}
struct CoreDataUserRepositoryFactory: UserRepositoryFactory {
func makeRepository() -> UserRepository {
return CoreDataUserRepository()
}
}
然后我想通过我所有的工厂传递一个依赖容器。
struct DependencyContainer {
let userRepositoryFactory: UserRepositoryFactory
}
我尝试的另一个解决方案是这样的:
protocol Repository2 {
associatedtype Item
func item(at index: Int) -> Item
}
class Repository2Base<Model>: Repository2 {
func item(at index: Int) -> Model {
fatalError()
}
}
class FakeRepository2<Model>: Repository2Base<Model> {
var items: [Model] = []
override func item(at index: Int) -> Model {
return items[index]
}
}
protocol UserRepositoryFactory2 {
func makeRepository() -> Repository2Base<User>
}
class FakeUserRepositoryFactory2: UserRepositoryFactory2 {
func makeRepository() -> Repository2Base<User> {
return FakeRepository2<User>()
}
}
现在它可以编译并且运行良好,但我不喜欢我必须调用fatalError()
来编译。 这似乎是一个黑客。 有没有一种优雅的方式来实现我的目标?
我不喜欢混合 inheritance 和 generics,我会说这是你必须调用fatalError()
的原因
尝试这个:
Repository
协议protocol Repository {
associatedtype Item: Identifiable
func item(at index: Int) -> Item
init()
}
class FakeRepository<Item: Identifiable>: Repository {
var items: [Item]
func item(at index: Int) -> Item {
return items[index]
}
required init() { // notice that it it required
items = []
}
}
我在这里添加了init()
因为我希望FakeRepositoryFactory
创建任何存储库
public class SomeFakeRepositoryFactory {
public init() { }
public func makeRepository<Repo: Repository>() -> Repo {
return Repo()
}
}
你可以这样使用它:
struct User: Identifiable {
let id: Int
let name: String
}
struct Car: Identifiable {
let id: Int
let name: String
}
let user = User(id: 1, name: "Robert")
let factory = SomeFakeRepositoryFactory()
var userRepository: FakeRepository<User> = factory.makeRepository()
userRepository.items = [user]
userRepository.items.forEach { print($0.name) } // prints "Robert"
let car = Car(id: 1, name: "Delorean")
var carRepository = factory.makeRepository() as FakeRepository<Car>
carRepository.items = [car]
carRepository.items.forEach { print($0.name) } // prints Delorean
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.