Я пытаюсь работать с протоколно-ориентированным программированием, насколько это возможно, и заинтригован непрозрачными типами в 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»Простые, глупые "принципы проектирования".