swift - 如何创建符合具有嵌套 PAT 的协议(protocol)的类?

标签 swift nested protocols type-erasure associated-types

我正在尝试使用类型删除来创建 Repository可以遵循的协议(protocol)(类似于 Swift 的 AnyCollection )。该协议(protocol)需要包装在类型删除的类中,因为它包含 PAT。

但是,由于该协议(protocol)有一个嵌套协议(protocol),该协议(protocol)也有一个 PAT,所以它使事情变得有些复杂。

  • Keyable是提供 key 的东西(最终我想确保也是 Hashable ...一次一件事)。
  • Repository是我的广义“容器”协议(protocol)
  • AnyKeyable Keyable 的类型删除包装
  • AnyRepository Repository 的类型删除包装

我有一个几乎编译的 Playground 片段:

protocol Keyable {
    associatedtype KeyType// where KeyType: Hashable
    func key() -> KeyType
}

protocol Repository {
    associatedtype DataType: Keyable
    func all() -> [DataType]
    func get(id: DataType.KeyType) throws -> DataType?
    func create(object: DataType) throws -> Bool
    func update(object: DataType) throws -> Bool
    func delete(object: DataType) throws -> Bool
    func clear() -> Bool
}

final class AnyKeyable<T>: Keyable {
    private let _key: () -> T
    init<U: Keyable>(_ keyable: U) where U.KeyType == T {
        _key = keyable.key
    }

    public func key() -> T {
        return _key()
    }
}

final class AnyRepository<T: Keyable>: Repository {
    private let _all: () -> [T]
    private let _get: (_ id: T.KeyType) throws -> T?
    private let _create: (_ object: T) throws -> Bool
    private let _update: (_ object: T) throws -> Bool
    private let _delete: (_ object: T) throws -> Bool
    private let _clear: () -> Bool

    init<U: Repository>(_ repository: U) where U.DataType == T {
        _get = repository.get
        _create = repository.create
        _delete = repository.delete
        _update = repository.update
        _clear = repository.clear
        _all = repository.all
    }

    func all() -> [T] {
        return _all()
    }

    func get<K: Keyable>(id: K.KeyType) throws -> T? where T.KeyType: Keyable, T.KeyType == K.KeyType {
        let anyKeyable = AnyKeyable(id)
        return try _get(anyKeyable)
    }

    func create(object: T) throws -> Bool {
        return try _create(object)
    }

    func update(object: T) throws -> Bool {
        return try _update(object)
    }

    func delete(object: T) throws -> Bool {
        return try _delete(object)
    }

    func clear() -> Bool {
        return _clear()
    }
}

final class Contact {
    var name: String = ""
    var email: String = ""
}

extension Contact: Keyable {
    public typealias KeyType = String
    public func key() -> String {
        return "im the key"
    }
}

// Just a dummy class to see 
final class ContactRepository: Repository {
    typealias DataType = Contact
    private var someContacts: [Contact] = []

    func all() -> [Contact] {
        return someContacts
    }

    func clear() -> Bool {
        someContacts.removeAll()
        return someContacts.count == 0
    }

    func get(id: Contact.KeyType) throws -> Contact? {
        return nil
    }

    func update(object: Contact) throws -> Bool {
        return false
    }

    func create(object: Contact) throws -> Bool {
        return false
    }

    func delete(object: Contact) throws -> Bool {
        return false
    }
}

// Testing
let i = AnyRepository<Contact>(ContactRepository())
i.all()
i.clear()

问题是 Swift 编译器提示我没有使用 Kget<K: Keyable>(id: K.KeyType) 的方法签名中......但在我看来,我确实是。

我的想法是编译器因为DataType.KeyType而提示声明get()在协议(protocol)中,而不是在 AnyRepository 中具体子类,但我不确定如何纠正这个问题以为编译器提供更多上下文。

有没有更好的方法来构建它以允许我完成这个模式?也允许第一个associatedtype怎么样?在Keyable成为associatedtype KeyType where KeyType: Hashable

非常感谢任何帮助, 谢谢!

最佳答案

正如 Hamish 指出的那样,没有必要引入另一个泛型 K。这让事情变得复杂了。清理完所有内容后,现在无需 AnyKeyable 类的复杂化即可工作:

public protocol Keyable {
    associatedtype KeyType where KeyType: Hashable
    func key() -> KeyType
}

public protocol Repository {
    associatedtype DataType: Keyable
    func all() -> [DataType]
    func get(id: DataType.KeyType) throws -> DataType?
    func create(object: DataType) throws
    func create(objects: [DataType]) throws
    func update(object: DataType) throws
    func delete(object: DataType) throws
    func delete(objects: [DataType]) throws
    func clear()
}

public class AnyRepository<T: Keyable>: Repository {
    private let _all: () -> [T]
    private let _get: (_ id: T.KeyType) throws -> T?
    private let _create: (_ object: T) throws -> Void
    private let _createAll: (_ objects: [T]) throws -> Void
    private let _update: (_ object: T) throws -> Void
    private let _delete: (_ object: T) throws -> Void
    private let _deleteAll: (_ objects: [T]) throws -> Void
    private let _clear: () -> Void

    public init<R: Repository>(_ repository: R) where R.DataType == T {
        _get = repository.get
        _create = repository.create
        _createAll = repository.create
        _delete = repository.delete
        _deleteAll = repository.delete
        _update = repository.update
        _clear = repository.clear
        _all = repository.all
    }

    public func all() -> [T] {
        return _all()
    }

    public func get(id: T.KeyType) throws -> T? {
        return try _get(id)
    }

    public func create(object: T) throws {
        return try _create(object)
    }

    public func create(objects: [T]) throws {
        return try _createAll(objects)
    }

    public func update(object: T) throws {
        return try _update(object)
    }

    public func delete(object: T) throws {
        return try _delete(object)
    }

    public func delete(objects: [T]) throws {
        return try _deleteAll(objects)
    }

    public func clear() {
        return _clear()
    }
}

关于swift - 如何创建符合具有嵌套 PAT 的协议(protocol)的类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47799938/

相关文章:

ios - Xcode 10 升级 : dyld library error on openssl. 框架/openssl

ios - 在 StackView 中的 UIImage 上添加 UILabel

java - 嵌套 Hashmap 覆盖其他键

javascript - 我应该使用什么协议(protocol)以及它的文档是什么?

IOS navigationBar 不在屏幕边缘

ios - 当节点下面的节点被删除时,节点不会移动

javascript - 根据顺序更改深层嵌套 Javascript 对象中同名属性的多个值

mysql - LEFT JOIN 运行速度非常慢,但嵌套 select 却不会

clojure 将协议(protocol)定义保存在与实现不同的命名空间中

iphone - 委托(delegate)方法不工作 ios