我相信我已经解决了这个问题,这要归功于当前的1指针答案和泛型。我将更新下面的答案,并将其添加到其中。谢谢!
试图创建命令总线
我正努力在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,所以无论我返回什么标量,都必须检查它们是否存在于创建命令并将其传递给命令总线的控制器中。所以-我能用泛型来解决我这里的任何问题吗?这更像是一个架构问题还是OO问题?或者我所追求的根本不可能,因为这是一种严格类型的语言吗?
我觉得这里有明显的东西我错过了。我如何创建我需要的,同时保持体面的打字,在那里,我不需要告诉编译器的每一步的方法。这有可能吗?
我试过的..。
有人建议我也可以使用带关联类型的协议,但我不确定该把它放在哪里,或者如何使用。我还想到了一个‘请求/响应’风格的东西,其中每个命令都返回一个响应,但这必须是一个协议,这基本上让我回到了Any问题上。
我还尝试将CommandBus签名更改为:public func retrieveHandler<T: CommandHandler>(_ command: Command) -> T。我现在必须向函数传递一个带有type声明的命令:
let handler: ConcreteHandlerName = commandBus.retrieveHandler(command)发布于 2018-09-04 11:00:15
我不确定这是否是你想要的,因为我不太明白你想要实现什么,但是看看我已经整理好的游乐场代码:
import Foundation
protocol CommandProtocol {
associatedtype Handler: CommandHandlerProtocol
associatedtype Result
}
protocol CommandHandlerProtocol {
associatedtype Command: CommandProtocol
func execute(_ command: Command) -> Command.Result
init()
}
class IntCommand: CommandProtocol {
typealias Handler = IntCommandHandler
typealias Result = Int
}
class IntCommandHandler: CommandHandlerProtocol {
typealias Command = IntCommand
func execute(_ command: Command) -> Command.Result {
return 5
}
required init() { }
}
class StringCommand: CommandProtocol {
typealias Handler = StringCommandHandler
typealias Result = String
}
class StringCommandHandler: CommandHandlerProtocol {
typealias Command = StringCommand
func execute(_ command: Command) -> Command.Result {
return "Hello!"
}
required init() { }
}
class CommandBus {
public var map: [String: Any] = [:]
func dispatch<T: CommandProtocol>(_ command: T) -> T.Handler.Command.Result {
let handlerClass = map[String(describing: type(of: command))] as! T.Handler.Type
let handler = handlerClass.init() as T.Handler
return handler.execute(command as! T.Handler.Command)
}
}
let commandBus = CommandBus()
commandBus.map[String(describing: IntCommand.self)] = IntCommandHandler.self
commandBus.map[String(describing: StringCommand.self)] = StringCommandHandler.self
let intResult = commandBus.dispatch(IntCommand())
print(intResult)
let stringResult = commandBus.dispatch(StringCommand())
print(stringResult)

更新
下面是一个CommandBus,它映射到处理程序的特定实例,而不仅仅是它的类型。这意味着可以从init中删除CommandHandlerProtocol方法。
此外,我还隐藏了map属性,而是添加了一个添加映射的方法。这样,在创建地图时,类型总是正确的:
protocol CommandHandlerProtocol {
associatedtype Command: CommandProtocol
func execute(_ command: Command) -> Command.Result
}
class CommandBus {
private var map: [String: Any] = [:]
func map<T: CommandProtocol>(_ commandType: T.Type, to handler: T.Handler) {
map[String(describing: commandType)] = handler
}
func dispatch<T: CommandProtocol>(_ command: T) -> T.Handler.Command.Result {
let handler = map[String(describing: type(of: command))] as! T.Handler
return handler.execute(command as! T.Handler.Command)
}
}
let commandBus = CommandBus()
commandBus.map(IntCommand.self, to: IntCommandHandler())
commandBus.map(StringCommand.self, to: StringCommandHandler())
let intResult = commandBus.dispatch(IntCommand())
print(intResult)
let stringResult = commandBus.dispatch(StringCommand())
print(stringResult)发布于 2018-08-29 19:59:20
associatedtype是你问题的答案:
protocol Command { }
protocol CommandHandler {
associatedtype CommandType: Command // the concrete `CommandType` must conform to `Command`
associatedtype ReturnType // each handler will define what the return type is
func execute(_ command: CommandType) -> ReturnType?
}
struct TestCommand: Command {
public let value = 1
}
struct TestCommandHandler: CommandHandler {
typealias CommandType = TestCommand
typealias ReturnType = Int
public func execute(_ command: CommandType) -> ReturnType? {
// now `command` is of type `TestCommand` and the return value is `Int?`
return 42
}
}我不知道你的CommandBus应该做什么,所以我跳过了你问题的那一部分。
https://stackoverflow.com/questions/52065876
复制相似问题