[Design Pattern] 커맨드 패턴, Command Pattern

nnnyeong·2021년 6월 11일
0

DesignPattern

목록 보기
5/9

커맨드 패턴은 객체들의 행위 자체를 클래스로 캡슐화 하여 다양한 행위 객체의 행위들이 추가되어도 유연하게 대응할 수 있도록 하는 패턴!

예를들어,

승헌쓰는 늘 영상 속에서 노래를 부르기 전 "기가지니 에스파의 넥스트레벨 틀어줘~" 이런식으로 주문을 거는데, 비슷한 장면으로 티피 프로그램이나 광고 속에서 "~야 에어컨 틀어줘", "~야 히터 틀어줘" 하는 경우를 많이 볼 수 있당

비슷하게 OKGoogle 에게 "에어컨 틀어줘" 라고 말하는 상황을 코드로 작성해
본다면,

적용 전

AirConditioner

class AirConditioner {
    func powerOn() -> Void {
        print("Air Conditioner power on")
    }
}

OKGoogle

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()

결과


그런데 만약에 계절이 흘러 날씨가 추워져 히터를 키고 싶다면?
에어컨과 마찬가지로 히터 객체를 만들어 오케이구글이 이를 새롭게 참조하고 히터인지, 에어컨인지 조건문으로 구분하고, 아니면 각 기기에 맞는 함수를 작성해 동작 시켜야 하겠지?


Heater

class Heater {
    func powerOn() -> Void {
        print("Heater power on~")
    }
}

OKGoogle

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 이 모두 상속할 상위 인터페이스를 정의하고

적용 후

Command

protocol Command {
    func run() -> Void
}

히터on, 에어컨on을 클래스화 하여 캡슐화 한다!

HeaterPowerOn, ACPowerOn

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 을 통해 히터와 에어컨을 동일한 방법으로 다룰 수 있다!

OKGoogle

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를 위반하지 않는다!!




참고 자료

profile
주니어 개발자까지 ☄️☄️

0개의 댓글