커맨드 패턴은 객체들의 행위 자체를 클래스로 캡슐화 하여 다양한 행위 객체의 행위들이 추가되어도 유연하게 대응할 수 있도록 하는 패턴!
예를들어,
승헌쓰는 늘 영상 속에서 노래를 부르기 전 "기가지니 에스파의 넥스트레벨 틀어줘~" 이런식으로 주문을 거는데, 비슷한 장면으로 티피 프로그램이나 광고 속에서 "~야 에어컨 틀어줘", "~야 히터 틀어줘" 하는 경우를 많이 볼 수 있당
비슷하게 OKGoogle 에게 "에어컨 틀어줘" 라고 말하는 상황을 코드로 작성해
본다면,
class AirConditioner {
func powerOn() -> Void {
print("Air Conditioner power on")
}
}
class OKGoogle {
private var ac = AirConditioner()
init(ac : AirConditioner) {
self.ac = ac
}
func runAC() -> Void {
self.ac.powerOn()
}
}
let airConditioner = AirConditioner()
let okGoogle = OKGoogle(ac: airConditioner)
okGoogle.runAC()
그런데 만약에 계절이 흘러 날씨가 추워져 히터를 키고 싶다면?
에어컨과 마찬가지로 히터 객체를 만들어 오케이구글이 이를 새롭게 참조하고 히터인지, 에어컨인지 조건문으로 구분하고, 아니면 각 기기에 맞는 함수를 작성해 동작 시켜야 하겠지?
class Heater {
func powerOn() -> Void {
print("Heater power on~")
}
}
class OKGoogle {
private var ac : AirConditioner!
private var heater : Heater!
init(ac : AirConditioner, heater : Heater) {
self.ac = ac
self.heater = heater
}
func runAC() -> Void {
self.ac.powerOn()
}
func runHeater() -> Void{
self.heater.powerOn()
}
}
let airConditioner = AirConditioner()
let heater = Heater()
let okGoogle = OKGoogle(ac: airConditioner, heater: heater)
okGoogle.runAC()
okGoogle.runHeater()
이런식이라면 곤란하다,
티비 키려면 또 티비 클래스 작성, 티비 추가, 티비 함수 작성
전등 키려면 또 전등 클래스 작성, 전등 추가, 전등 함수 작성 ;;
that's no no,,
대신,
'에어컨을 켜줘', '히터 켜줘' 라는 명령 자체를 클래스로 만들어서 캡슐화 하고 에어컨, 히터를 가동시키는 오케이 구글은 두 명령을 동일한 방법으로 다룰 수 있도록 하자!
먼저 히터on, 에어컨on 이 모두 상속할 상위 인터페이스를 정의하고
protocol Command {
func run() -> Void
}
히터on, 에어컨on을 클래스화 하여 캡슐화 한다!
class HeaterPowerOn : Command {
private var heater : Heater!
init(heater : Heater) {
self.heater = heater
}
func run() {
heater.powerOn()
}
}
class ACPowerOn : Command {
private var ac : AirConditioner!
init(ac : AirConditioner) {
self.ac = ac
}
func run() {
ac.powerOn()
}
}
이렇게 되면 오케이구글은 Command만을 알고 있으면 Command.run 을 통해 히터와 에어컨을 동일한 방법으로 다룰 수 있다!
class OKGoogle {
private var command : Command!
init(command : Command) {
self.command = command
}
func work() {
self.command.run()
}
}
let heaterPowerOn = HeaterPowerOn(heater: Heater())
let acPowerOn = ACPowerOn(ac: AirConditioner())
let okGoogle = OKGoogle(command: heaterPowerOn)
okGoogle.work()
okGoogle.setCommand(command: acPowerOn)
okGoogle.work()
다이어그램을 보면 OKGoogle은 구체적인 클래스인 HeaterPowerOn, ACPowerOn 등을 직접적으로 알 필요가 없고, 추상적 레벨인 Command 에만 접근하면 된다!
또! 만약 뭐 스탠드나, 냉장고 등을 동작시키는 행위를 추가 적용하고 싶을 때에도 OKGoogle의 코드 변경 없이 확장이 가능하여 OCP를 위반하지 않는다!!