단순히 글로된 설명을 봐선 잘 와닿지 않는다. 백문이 불여일견이라고 예제를 한 번 작성해보자.
먼저 불을 켜는 기능을 만들어 봅시다.
class Lamp {
constructor(){
this.isTurnOn = false;
}
turnOn(){
if(this.isTurnOn){
console.log('이미 불이 켜졌습니다.');
return;
}
this.isTurnOn = true;
console.log('불을 켰습니다.');
}
}
Button 클래스를 만들어서 전등을 켤 수 있도록 합니다.
class Button {
constructor(lamp) {
this.lamp= lamp;
}
execute() {
this.lamp.turnOn();
}
}
const lamp = new Lamp();
const button = new Button(Lamp);
button.execute(); // 불을 켰습니다.
그렇다면 스피커를 켜는 기능을 추가하고싶을땐 어떻게 할까?
class Speaker{
constructor(){
this.isPowerOn = false;
}
powerOn() {
if(this.isPowerOn ){
console.log('이미 스피커가 켜졌습니다.');
return;
}
this.isPowerOn = true;
console.log('스피커를 켰습니다.');
}
}
class Button {
constructor(lamp, speaker) {
this.modes = {lamp, speaker};
this.mode = null;
}
setMode(mode){
this.mode = this.modes[mode];
}
execute() {
switch(this.mode){
case lamp:
this.mode.turnOn();
break;
case speaker:
this.mode.powerOn();
break;
}
}
}
const lamp = new Lamp();
const speaker = new Speaker();
const button = new Button(Lamp, Speaker);
button.setMode(lamp);
button.execute(); // 불을 켰습니다.
button.setMode(speaker);
button.execute(); // 스피커를 켰습니다.
이렇게 새로운 기능이 추가될 때 마다 Button 클래스에 기능을 추가해야한다. 기존의 execute 메소드에서 case가 늘어나게 될 것이다.
이럴때 Command pattern을 사용한다면 어떻게 될까?
간단하게 커맨드 클래스에 execute 메소드를 추가합니다.
class Command {
execute();
}
그 다음 전등을 끄고 켤 수 있는 메소드를 Lamp 클래스에 작성한다.
class Lamp{
constructor() {
this.isTurnOn = false;
}
on() {
if (this.isTurnOn) {
console.log('이미 불이 켜져있습니다.');
return;
}
this.isTurnOn = true;
console.log('불을 켰습니다.');
}
off() {
if (!this.isTurnOn) {
console.log('이미 불이 꺼져있습니다.');
return;
}
this.isTurnOn = false;
console.log('불을 껐습니다.');
}
}
다음으로 Lamp를 켜는 명령을 클래스화 하여 LampOnCommand 클래스를 작성한다.
Command 클래스를 상속 받는다.
class LampOnCommand extends Command {
constructor(light) {
super();
this.light = light;
}
execute() {
this.light.on();
}
}
Button 클래스를 생성하여 커맨드를 지정해주는 setCommand와 해당 command를 실행해주는 execute 함수를 만들어준다.
class Button {
constructor() {
this.command = null;
}
setCommand(command) {
this.command = command;
}
execute() {
this.command.execute();
}
}
Speaker 클래스도 Lamp 클래스와 동일하게 만들어준 뒤 실행시킬 파일에 코드를 작성합니다.
const lamp = new Lamp();
const speaker = new Speaker();
const lampOnCommand = new LampOnCommand(lamp);
const speakerOnCommand = new SpeakerOnCommand(speaker);
const button = new Button();
button.setCommand(lampOnCommand);
button.execute(); // 불을 켰습니다.
button.setCommand(speakerOnCommand);
button.execute(); // 스피커를 켰습니다.
이렇게 되면 Button 클래스에 기능을 따로 추가할 필요 없이 새로운 command를 계속 만들어서 command를 설정해주기만 하면 된다.
이렇게 해주면 객체간의 의존성이 없어진다.