Command Pattern

최정은·2020년 12월 12일
0

Command pattern이란?

  • 실행될 기능(메소드)을 캡슐화하여 주어진 기능을 실행할 수 있는 재사용성이 높은 클래스 설계를 의미한다.
  • 이벤트 발생시 다양한 기능을 수행 및 수정하고싶을 때 이벤트를 발생시키는 클래스를 변경시키지 않고 재사용하고자 할 때 유용하다.
  • 의존성을 제거할 수 있다.

단순히 글로된 설명을 봐선 잘 와닿지 않는다. 백문이 불여일견이라고 예제를 한 번 작성해보자.

먼저 불을 켜는 기능을 만들어 봅시다.

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를 설정해주기만 하면 된다.

이렇게 해주면 객체간의 의존성이 없어진다.

0개의 댓글