Мне кажется, я решил эту проблему благодаря текущему ответу 1pointer и обобщению. Я обновлю ответ ниже и добавлю его к этому. Спасибо!
Попытка создать командную шину
Я изо всех сил пытаюсь создать командную шину в Swift. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что я пытаюсь сделать эту вещь достаточно общей для обработки различных команд, но в результате во многих случаях мне приходится возвращать Any
, а это значит, что мне придется выполнение проверок в коде для всего, и я не уверен, что я могу с этим поделать.
Код
protocol Command { }
struct TestCommand: Command
{
public let value: int = 1
}
protocol CommandHandler
{
func execute(_ command: Command) -> Any?
}
struct TestCommandHandler: CommandHandler
{
public func execute(_ command: Command) -> Any?
{
// First problem - I have to cast command as! TestCommand
// due to the interface
}
}
И, наконец, ниже находится командная шина, которая в основном просто отображает данную ему команду обработчику и возвращает ее. Ничего особенного или сложного.
struct CommandBus
{
public let container: Container
/// Added as closures so the commands are only resolved when requested
private func commandMap() -> Array<[String: () -> (Any)]>
{
// TestCommand implements an empty protocol CommandProtocol
return [
[String(describing: TestCommand.self): { self.container.resolve(TestCommandHandler.self)! }]
]
}
/// Dispatch a command to the relevant command handler
public func dispatch(_ command: Command) -> Any
{
let map = self.commandMap()
let commandName = String(describing: command.self)
let element = map.enumerated().first(where: { $0.element.keys.first == commandName })
let elementIndex = map.index(element!.offset, offsetBy: 0)
let commandHandler: CommandHandler = map[elementIndex].first!.value() as! CommandHandler
return commandHandler.execute(command)!
}
}
Единственная обязанность командных шин - выяснить, какой обработчик команд вызывать, и вернуть результат.
Проблема
Вот проблемы с тем, что у меня сейчас есть:
- Конкретный обработчик команд всегда должен будет проверить, что объект, который я передаю, имеет определенный конкретный тип, и привести его к
as
, чтобы я мог его использовать ( в этом случае обработчик хочет получить value
из команды)
- Командная шина всегда возвращает
Any
, поэтому любые скаляры, которые я возвращаю, должны проверять их наличие в контроллере, который создает команду и передает ее на командную шину
Итак, могу ли я использовать дженерики для решения любых моих проблем? Это скорее проблема архитектуры или ОО? Или то, что я ищу, в принципе невозможно, потому что это строго типизированный язык?
Я думаю, что здесь что-то очевидно, что мне не хватает. Как я могу создать то, что мне нужно, при этом сохраняя приличную типизацию, когда мне не нужно рассказывать компилятору все на каждом этапе пути. Это вообще возможно?
Что я пробовал ...
Кто-то предложил мне также использовать протоколы со связанными типами, но я не уверен, куда именно это поставить или как это сделать. Я также подумал о стиле «запрос / ответ», в котором каждая команда возвращает ответ, но это должен быть протокол, который в основном возвращает меня к проблеме Any
.
Я также попытался изменить подпись CommandBus
на: public func retrieveHandler<T: CommandHandler>(_ command: Command) -> T
. Теперь я должен передать команду функции с объявлением типа:
let handler: ConcreteHandlerName = commandBus.retrieveHandler(command)