Swift: Могу ли я иметь протокол, который наследует от протокола и ограничивает его? - PullRequest
1 голос
/ 14 июня 2019

Скажем, у меня есть следующие протоколы:

protocol RateableItem {
   var identifier: String { get }  // placeholder. This could be a lot of properties
   var name: String { get set }
   var rating: Int { get set }
}

protocol RateableItemManager {

    /// get some objects matching query criteria
    func objects(matching query: RateableItemQuery) -> [RateableItem]

    /// get a specific object
    func object(withID identifier: String) -> RateableItem?

    /// persists them
    func save(_ object: RateableItem) throws

    /// deletes the objects.
    func delete(_ objects: [RateableItem])

    /// creates a new object.
    func create() -> RateableItem
}

и

struct RateableItemQuery {
    let searchPredicate: NSPredicate?  // nil means all
    let sortingBlock: ((RateableItem, RateableItem) throws -> Bool)?
    let groupingSpecifier: (() -> String)?
    init(matching predicate: NSPredicate? = nil,
         sort: ((RateableItem, RateableItem) throws -> Bool)? = nil,
         groupBy: (() -> String)? = nil) {

        self.searchPredicate = predicate
        self.sortingBlock = sort
        self.groupingSpecifier = groupBy
    }
}

Теперь я могу реализовать конкретный тип этого, который возвращает конкретные типы, которые соответствуют протоколу.Конкретные типы, которые возвращаются, не имеют отношения к остальной части моего кода, потому что остальная часть кода заботится только о том, чтобы они соответствовали протоколу.Это позволяет мне делать «производственные» и «фиктивные» версии моделей.

Есть ли способ, которым я могу определить это более широко, например:

struct Query<T> {
    let searchPredicate: NSPredicate?  // nil means all
    let sortingBlock: ((T, T) throws -> Bool)?
    let groupingSpecifier: (() -> String)?
    init(matching predicate: NSPredicate? = nil,
         sort: ((T, T) throws -> Bool)? = nil,
         groupBy: (() -> String)? = nil) {

        self.searchPredicate = predicate
        self.sortingBlock = sort
        self.groupingSpecifier = groupBy
    }
}

, такой, что

struct RateableItemQuery: Query<RateableItem> {}

и

protocol ItemManager<T> {

    func objects(matching query: Query<T>) -> [T]

    func object(withID identifier: String) -> T?

    func save(_ object: T) throws

    func delete(_ objects: [T])

    func create() -> T
}

и

protocol RateableItemManager: ItemManager<RateableItem>

Поскольку я хочу использовать эту парадигму API, но не обязательно ограничивать что-либо на базовом уровнеуровня протокола, поскольку я часто просто переписываю эти сигнатуры методов для различных типов протоколов, с которыми я бы хотел работать.

Если я не ошибаюсь, связанные типы должны быть конкретными,делая их возвращаемые типы также конкретными, и тогда я не могу легко работать с типами протоколов.

Извините, если я не говорил "канонически".Надеюсь, я смог передать свои намерения.

Может ли это быть тем, что грядущий Swift 5.1 предлагает с точки зрения непрозрачных типов, возвращая -> некоторый ProtocolType?

1 Ответ

0 голосов
/ 03 июля 2019

Получается, что непрозрачные типы тоже не являются ответом.

Я решил свою проблему, создав абстрактные базовые классы для менеджеров, Query и QueryResults были общими структурами, а конкретные подклассы для менеджеров могли приниматьи вернуть типы данных на основе протокола.

public struct Query<T> {

    var searchPredicate: NSPredicate?
    var sortingBlock: ((T, T) throws -> Bool)?
    var groupingSpecifier: (() -> String)?

    var results: QueryResults<T>?

    init(matching predicate: NSPredicate?,
         sort: ((T, T) throws -> Bool)?,
         groupBy: (() -> String)?) {

    }
}


public struct QueryResults<T> {

    public enum ChangeType: UInt {
        case insert = 1
        case delete
        case move
        case update
    }

    public struct Section<T> {
        var items: [T]
        var title: String?
    }

    public var sections: [Section<T>] = []

    public func object(at indexPath: IndexPath) -> T? {
        return nil
    }
}

public class AnyObjectManager<ObjectType> {

    public enum Error: Swift.Error {
        case abstractImplementationRequiresOverride
    }

    typealias QueryDidChangeObjectBlock = ((
        _ query: Query<ObjectType>,
        _ didChangeObject: ObjectType,
        _ atPath: IndexPath?,
        _ forChangeType: QueryResults<ObjectType>.ChangeType,
        _ newIndexPath: IndexPath?) -> Void)

    typealias QueryDidChangeSectionBlock = ((
        _ query: Query<ObjectType>,
        _ didChangeSection: QueryResults<ObjectType>.Section<ObjectType>,
        _ atSectionIndex: Int,
        _ forChangeType: QueryResults<ObjectType>.ChangeType) -> Void)

    /// get some objects matching query criteria.  nil means return all
    func objects(matching query: Query<ObjectType>?) -> [ObjectType] {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// get a specific object
    func object(withID identifier: String) -> ObjectType? {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// deletes the objects.  Does it commit that to disk?
    func remove(_ objects: [ObjectType]) {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// creates a new object but does not save it.
    func create() -> ObjectType {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    /// this is basically to mimic the functionality of a fetched results controller...
    func monitorQuery(_ query: Query<ObjectType>,
                      willChangeBlock: ((_ query: Query<ObjectType>) -> Void)?,
                      didChangeObjectBlock: QueryDidChangeObjectBlock?,
                      didChangeSectionBlock: QueryDidChangeSectionBlock?,
                      didFinishChangesBlock:((_ query: Query<ObjectType>) -> Void)?) {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }
    /// and this is to stop monitoring that.
    func stopMonitoringQuery() {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    public func saveChanges() throws {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }

    public func discardChanges() throws {
        fatalError("Abstract implementation.  You need to override this method and provide an implementation!")
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...