If you command wisely, you’ll be obeyed cheerfully. — Thomas Fuller
The command pattern is used to encapsulate details of how to invoke a method on an object in a way that allows the method to be invoked at a different time or by a different component. (Design Pattern in Swift)
protocol Command {
func execute(receiver: Any)
}
//: A generic command can handle different kind of commands
class GenericCommand<T>: Command {
private var instruction: T -> Void
init(instruction: T -> Void){
self.instruction = instruction
}
func execute(receiver: Any) {
guard let safeReceiver = receiver as? T else {
fatalError("Receiver is not an expected type")
}
instruction(safeReceiver)
}
class func createCommand(instruction: T -> Void) -> Command {
return GenericCommand(instruction: instruction)
}
}
protocol LatestCommand {
func executeLatestCommand(receiver: Any)
}
//: A commandWrapper manages the execution of multiple commands or the lastest command
class CommandWrapper: Command, LatestCommand {
private let commands: [Command]
private var remainingCommands:[Command]
init(commands: [Command]){
self.commands = commands
self.remainingCommands = commands
}
func execute(receiver: Any) {
print("will excute all commands for \(receiver)")
commands.map{$0.execute(receiver)}
}
func executeLatestCommand(receiver: Any){
print("will execute the lastest command for \(receiver)")
remainingCommands.removeLast().execute(receiver)
print("\(remainingCommands.count) commands left.")
}
}
class PeopleStandByCommand: Command {
func execute(receiver: Any) {
print("All people standby")
}
}
class PrepareWeaponCommand: Command {
func execute(receiver: Any) {
print("Weapons are ready")
}
}
class PrepareResourcesCommand: Command {
func execute(receiver: Any) {
print("All resources are ready")
}
}
//: An operation that has manages the preparation of the war. It creates multiple commands which will be executed at runtime
class PrepareForWarOperation:CustomStringConvertible {
private var commands:[Command] = []
private var queue = dispatch_queue_create("arrayQueue", DISPATCH_QUEUE_SERIAL)
var name: String
var description: String { return name }
init(kindom: String){
print("PrepareForWar Operation for \(kindom) is created")
self.name = kindom
prepareEverything()
}
func notifyPeople(people: String){
print("Notified \(people) about the war")
}
func prepareEverything(){
addCommand(PrepareForWarOperation.notifyPeople, people: "people of \(name)")
commands.append(PrepareResourcesCommand())
commands.append(PrepareWeaponCommand())
commands.append(PeopleStandByCommand())
}
private func addCommand(action: PrepareForWarOperation -> String -> Void, people: String){
dispatch_sync(queue) { () -> Void in
self.commands.append(GenericCommand.createCommand({ prep in
action(prep)(people)
}))
}
}
func getACommandWrap() -> protocol <Command,LatestCommand>? {
var command: protocol <Command,LatestCommand>?
dispatch_sync(queue) { () -> Void in
command = CommandWrapper(commands: self.commands)
}
return command
}
}
//: Testing
let p1 = PrepareForWarOperation(kindom: "Daria")
let commands = p1.getACommandWrap()
commands?.executeLatestCommand(p1)
commands?.execute(p1)
print("\n")
let p2 = PrepareForWarOperation(kindom: "Secila")
print("Use the same operation commands for another kindom called Secila")
commands?.execute(p2)