您需要做的是使用类型擦除,就像AnyHashable
在 Swift 标准库中。
你不能这样做:
var a: [Hashable] = [5, "Yo"]
// error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
你要做的就是使用类型擦除类型AnyHashable
:
var a: [AnyHashable] = [AnyHashable(5), AnyHashable("Yo")]
a[0].hashValue // => shows 5 in a playground
所以你的解决方案是首先将协议分成更小的部分并推广Equatable
to Hashable
(重复使用AnyHashable
)
protocol Conditionable {
var condition: Condition? { get set }
}
protocol Executable {
func execute() -> SKAction
}
protocol Commandable: Hashable, Executable, Conditionable {}
然后创建一个AnyCommandable
结构体,像这样:
struct AnyCommandable: Commandable, Equatable {
var exeBase: Executable
var condBase: Conditionable
var eqBase: AnyHashable
init<T: Commandable>(_ commandable: T) where T : Equatable {
self.condBase = commandable
self.exeBase = commandable
self.eqBase = AnyHashable(commandable)
}
var condition: Condition? {
get {
return condBase.condition
}
set {
condBase.condition = condition
}
}
var hashValue: Int {
return eqBase.hashValue
}
func execute() -> SKAction {
return exeBase.execute()
}
public static func ==(lhs: AnyCommandable, rhs: AnyCommandable) -> Bool {
return lhs.eqBase == rhs.eqBase
}
}
然后你可以像这样使用它:
var a = FunctionCommand()
a.commands = [AnyCommandable(MoveCommand()), AnyCommandable(FunctionCommand())]
并且您可以轻松访问的属性commands
, 因为AnyCommandable
实施Commandable
a.commands[0].condition
您需要记住现在添加Hashable
and Equatable
听从您的所有命令。
我使用这些实现进行测试:
struct MoveCommand: Commandable {
var movingVector: CGVector!
var condition: Condition?
func execute() -> SKAction {
return SKAction()
}
var hashValue: Int {
return Int(movingVector.dx) * Int(movingVector.dy)
}
public static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
return lhs.movingVector == rhs.movingVector
}
}
struct FunctionCommand: Commandable {
var commands = [AnyCommandable]()
var condition: Condition?
func execute() -> SKAction {
return SKAction.group(commands.map { $0.execute() })
}
var hashValue: Int {
return commands.count
}
public static func ==(lhs: FunctionCommand, rhs: FunctionCommand) -> Bool {
return lhs.commands == rhs.commands
}
}