我正在尝试在我的项目中实现仓储模式(repository pattern)。我希望该设计足够灵活,以便将来可以更换底层实现。例如,我想支持 Realm,但如果需要,也能够更换为 Core Data。我还想为测试实现伪造对象(fakes)。
我从伪造对象(fakes)开始实现,并且我想要实现一个具有泛型约束并符合 Repository 协议的 FakeRepository 基类。
请注意,我省略了一些实现细节,以使文章尽可能简短。
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]
}
}
我还希望为我打算支持的每个模型定义一个协议,例如UserRepository。
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()
才能编译通过的方式,这似乎是一种hack。有没有更优雅的方式来实现我的目标?