Почему я получаю ошибку компилятора для моего связанного типа в моем непрозрачном типе в Swift? - PullRequest
0 голосов
/ 30 июня 2019

Я пытаюсь работать с протоколно-ориентированным программированием, насколько это возможно, и заинтригован непрозрачными типами в Swift 5. Скажем, у меня есть:

public protocol Model {
  var identifier: String { get }
}

, а затем:

public protocol Query {
    associatedtype ObjectType: Model
    var predicate: NSPredicate? { get }
    var results: [ObjectType] { get }
    // everything omitted for brevity...
}

И хочу управлять вещами:

public protocol ObjectManager {
    associatedtype QueryType: Query

    /// get some objects matching query criteria.  nil means return all
    func objects(matching query: Self.QueryType?) -> [Self.QueryType.ObjectType]
}

Но в моем приложении много типов объектов, а значит, много менеджеров.

Итак, я хочу использовать синтаксический подход ObjectManager, но они должны иметь разновидности для различных типов значений, которые у меня будут.В этом случае давайте возьмем Книги.Давайте назовем их BookItems.

public protocol BookItem: Model {
    var title: String { get }
}

Итак, BookItem нуждается в запросе, прежде чем мы сможем создать для него менеджера:

public protocol BookItemQuery: Query where ObjectType: BookItem {

}

И теперь мы немного ограничим ObjectManager, чтобыBookItemManager (протокол)

public protocol BookItemManager: ObjectManager where QueryType: BookItemQuery {

    func createItem(with title: String, authorName: String) throws -> BookItem  // adds it to the Library
}

Я также хочу быть в состоянии быть независимым от среды хранения, либо для целей тестирования, либо потому, что, возможно, я хочу избавиться от Core Data в пользу Realm.io, например,Я не хочу переписывать свою кодовую базу, поэтому предпочитаю работать с протоколно-ориентированным программированием.Поэтому я подумал, что создам всеобъемлющий «LibraryManager», который может быть конкретным, но даже здесь я не уверен, где этот материал на основе протокола должен начинаться и заканчиваться.Кажется, что то, что я воображаю, невозможно?

В любом случае, я определяю базовый класс, в котором прямые подклассы будут реализовывать специфичные для персистентной структуры вещи, и это единственный раз, когда я буду иметь дело с конкретными типами ... внутриэти менеджеры.В противном случае я ожидал бы иметь дело с протокольно-ориентированными типами, причем их конкретный тип не имеет значения (ЭТО МОЯ НАДЕЖДА ... СКАЖИ МНЕ, ЕСЛИ Я НЕПРАВИЛЬНО ...?):

public class AnyLibraryManager {

    public enum Error: Swift.Error {
        case abstractImplementationRequiresOverride
    }

    @available(iOS 13.0.0, *)
    public var bookItemManager: some BookItemManager {
        return NullBookItemManager()  // purely to prevent the compiler complaints. You should subclass
    }

    @available(iOS 13.0.0, *)
    public var tagManager: some TagManager {
        return NullTagManager()  // purely to prevent the compiler complaints. You should subclass
    }

    @available(iOS 13.0.0, *)
    public var listManager: some ListManager {
        return NullListManager() // purely to prevent the compiler complaints. You should subclass
    }

    @available(iOS 13.0.0, *)
    public var authorManager: some AuthorManager {
        return NullAuthorManager()  // purely to prevent the compiler complaints. You should subclass
    }
}

Я былпохлопывая себя по спине, чтобы добраться до этой точки.Теперь я решил проверить, что я сделал.

func testCreateNewBookItemThenDeleteIt() {

        do {

            let item = try libraryManager.bookItemManager.createItem(with: "MyBook", authorName: "Stephen O")  // works....

            try libraryManager.bookItemManager.saveChanges()  // all good...
            var allItems = libraryManager.bookItemManager.objects(matching: nil)  // compiler also doesn't complain... 
            XCTAssertTrue(allItems.count == 1, "There should be only 1 item in the library")


            // THIS IS THE OFFENDING LINE, see below:
            try libraryManager.bookItemManager.remove([item])

            try libraryManager.bookItemManager.saveChanges()
            allItems = libraryManager.bookItemManager.objects(matching: nil)
            XCTAssertTrue(allItems.count == 0, "There should be no items in the library")

        } catch let error {
            XCTFail("Test failed due to error: \(error.localizedDescription)")
        }
    }

Итак, когда он доходит до этой строки:

try libraryManager.bookItemManager.remove([item])

Я получаю предупреждение компилятора:

Cannot convert value of type '[BookItem]' to expected argument type '[(some BookItemManager).QueryType.ObjectType]'

Но BookItemManager говорит, что его QueryType является BookItemQuery, поэтому ObjectType является BookItem.Так что же дает?Это ошибка, потому что непрозрачные типы являются новыми, и, возможно, компилятор не готов к этому сценарию?

Это отстой, потому что я продолжаю тратить время на работу с технологиями, которые в конечном итоге заставляют меня вернуться к ООП и «Keep It»Простые, глупые "принципы проектирования".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...