繁体   English   中英

如何实现符合具有类型约束的协议的泛型基类并为这些泛型类型实现工厂?

[英]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()的原因

尝试这个:

  1. 首先是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创建任何存储库

  1. 然后工厂:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM