如何创建符合嵌套PAT协议的类?

时间:2017-12-13 18:33:10

标签: swift nested protocols type-erasure associated-types

我尝试使用类型删除来创建一个可以符合的Repository协议(类似于Swift' s AnyCollection)。此协议需要包装在类型擦除的类中,因为它包含PAT。

但是,由于此协议具有嵌套协议,该协议也具有PAT,因此有些复杂化。

  • Keyable是一个提供密钥的东西(最终我也希望确保Hashable ......一次只做一件事。)
  • Repository是我的广义容器"协议
  • AnyKeyable Keyable
  • 的类型擦除包装器
  • AnyRepository Repository
  • 的类型擦除包装器

我有一个 ALMOST 编译的游乐场代码段:

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编译器抱怨我在K的方法签名中没有使用get<K: Keyable>(id: K.KeyType) ...但它看起来像我一样。

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

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

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

1 个答案:

答案 0 :(得分:0)

正如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()
    }
}