Фон
У меня есть приложение, которое взаимодействует с рядом аппаратных устройств. Все эти устройства имеют некоторые базовые функциональные возможности, но они также могут иметь 0 или более «возможностей» (помимо их базовой функциональности).
В настоящее время существует что-то вроде 30+ «других» возможностей, которые эти устройства могут"иметь
Кроме того, чтобы усложнить проблему, реализация возможностей на одном устройстве не одинакова на другом устройстве (мир просто так не работает ?)
Проблема
Я пытаюсь разработать решение API, которое позволило бы мне более легко управлять этим разнообразным набором функций.
Первоначально я пытался создать protocol
для каждой возможности. и затем приведите каждую реализацию устройства в соответствие с этими protocol
s.
Излишне говорить, что это беспорядок. Поскольку многие из этих возможностей не известны до времени выполнения, и мне нужно будет создать класс для каждой возможной комбинации возможностей, который не работает.
Кроме того, чтобы приложение могло определить,У устройства есть возможность, мне нужно использовать if instance is capable
и if let capability = instance as? Capability
, где угодно, что не всегда красиво или не всегда практично.
Некоторые возможности изменчивы, и приведенное выше просто не вызывает никаких предупреждений компилятора.
Есть и другие недостатки, которые требуют от меня компрометации дизайна, например, чтобы уменьшить количество реальных классов, я должен был включить опции «действительно поддерживает» во многих извозможности, как экземпляр устройства «может» поддерживать возможность, но не знаю, пока я не интегрирую устройство во время выполнения.
«A» решение
Затем я подумал, что яочень хотелось бы сделать, это изолировать функциональность возможностей в их собственных объектах, отделить их от самих базовых устройств, чтобы я мог «попросить» устройствоэкземпляр возможности, и если бы он был способен поддерживать его, он бы возвратил экземпляр, в противном случае он вернул бы nil
Что-то вроде:
if let capability = device.capability(for: Capabilities.toggableSetting) {...}
Желание было быиспользовать enum
для представления поддерживаемых возможностей, поскольку это становится самодокументируемым, самопроверкаемым, легко читаемым и понятным.
С этой целью я пришел к очень быстрой идее, которая выглядела примерно как
protocol Capability { }
protocol ToggableSetting: Capability {
func set(value: String) //{}
}
enum Capabilities {
case toggableSetting
}
protocol Device {
func capability<T: Capability>(for capability: Capabilities) -> T?
}
class SomeImplementationOfToggableSetting: ToggableSetting {
func set(value: String) {
// NOOP
}
}
class SomeDevice: Device {
let toggableSetting = SomeImplementationOfToggableSetting()
func capability<T>(for capability: Capabilities) -> T? where T : Capability {
switch capability {
case .toggableSetting: return toggableSetting as? T
}
return nil
}
}
let test = SomeDevice()
let ts: ToggableSetting? = test.capability(for: .toggableSetting)
ts
Как некоторые из вас сразу увидят, это не будет работать, так как это приведет к
Тип протокола 'ToggableSetting' не может соответствовать 'Capability', потому что только конкретные типыможет соответствовать протоколам
при let ts: ToggableSetting? = test.capability(for: .toggableSetting)
Итак, после некоторого прочтения и исследования, у меня появилась идея ввести «базовый» класс ...
class AnyToggableSetting: ToggableSetting {
func set(value: String) { fatalError("Not yet implemented") }
}
Что нужно реализовать ...
class SomeImplementationOfToggableSetting: AnyToggableSetting {
override func set(value: String) {
// NOOP
}
}
И тогда я мог бы использовать что-то вроде ...
let ts: AnyToggableSetting? = test.capability(for: .toggableSetting)
Хорошо, это работает, ноне так чисто, как хотелось бы.
хотелось бычтобы максимально разделить реализацию и работать с protocol
, поэтому мой код говорит: «Я хочу что-то, что подтверждает тоже, сказанное protocol
», и «не то, что ДОЛЖНО расширяться от AnySomething
»
Есть другие проблемы с дизайном, которые я хотел бы избежать, если это возможно (свойства не наследуются, например)
«Вопрос»
Итак, основной вопрос в том,есть ли способ, которым я могу использовать этот стиль "фабрики", чтобы я мог получить protocol
по сравнению с необходимостью предоставления подставки Any
в объекте?