# 커맨드 (Command) 패턴

# 커맨드 패턴이란 무엇인가요?

실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자 클래스와 실제 기능을 실행하는 수신자 클래스 사이의 의존성을 제거해 주는 패턴입니다.
즉, 실행될 기능이 변경되어도 호출자 클래스의 변경없이 그대로 사용할 수 있도록 해주는 패턴입니다.

  • UML
    커맨드패턴UML

# 예시 코드

  • 버튼을 눌렀을 때 불이 켜지는 기능을 수행하는 프로그램
public class Lamp {
    public void turnOn() {
        System.out.println("Lamp On"); 
    }
}
public class Button {   // Invoker
    private Lamp theLamp;
    public Button(Lamp theLamp) { this.theLamp = theLamp; }
    public void pressed() { 
        theLamp.turnOn(); 
    }
}
public class Client { //Receiver
    public static void main(String[] args) {
        Lamp lamp = new Lamp();
        Button lampButton = new Button(lamp);
        lampButton.pressed();
    }
}

버튼을 눌렀을 때 다른 기능을 수행하게 하고 싶으면 Button 클래스를 수정해야한다.

  • 불키는 기능을 알람 기능으로 수정
public class Alarm {
    public void start(){ System.out.println("Alarming"); }
}
public class Button {
    private Alarm theAlarm;
    public Button(Alarm theAlarm) { this.theAlarm = theAlarm; }
    public void pressed() { 
        theAlarm.start(); 
    }
}
public class Client {
    public static void main(String[] args) {
        Alarm alarm = new Alarm();
        Button alarmButton = new Button(alarm);
        alarmButton.pressed();
    }
}
  • 불키는 기능과 알람 기능을 둘 다 사용할 수 있게 수정
enum Mode { LAMP, ALARM };

public class Button {
    private Lamp theLamp;
    private Alarm theAlarm;
    private Mode theMode;
    public Button(Lamp theLamp, Alarm theAlarm) {   //기능클래스들을 인자로 입력받음
        this.theLamp = theLamp;
        this.theAlarm = theAlarm;
    }
    public void setMode(Mode mode) { // 원하는 모드를 설정
        this.theMode = mode;
    }
    public void pressed() { // 설정된 모드에 따라 기능실행
        switch (theMode) {
            case LAMP:
                theLamp.turnOn();
                break;
            case ALARM:
                theAlarm.start();
                break;
        }
    }
}

필요한 기능을 추가할 때마다 Button클래스를 수정해줘야한다는 불편함이 있습니다. 이를 해결하기 위해, 커맨드 패턴을 사용해보겠습니다.

  • Command
public interface Command {
    public abstract void execute();
}
  • ConcreteCommand
public class Lamp {
    public void turnOn() {
        System.out.println("Lamp On"); 
    }
    public void trunOff() {
        System.out.println("Lamp Off");
    }
}
public class LampOnCommand implements Command {
    private Lamp theLamp;
    public LampOnCommand(Lamp theLamp) { this.theLamp = theLamp; }
    public void execute() { 
        theLamp.turnOn(); 
    }
}
public class LampOffCommand implements Command {
    private Lamp theLamp;
    public LampOffCommand(Lamp theLamp) { this.theLamp = theLamp; }
    public void execute() {
        theLamp.trunOff();
    }
}

public class Alarm {
    public void start() {
        System.out.println("Alarming");
    }
}
public class AlarmStartCommand implements Command {
    private Alarm theAlarm;
    public AlarmStartCommand(Alarm theAlarm) { this.theAlarm = theAlarm; }
    public void execute() {
        theAlarm.start();
    }
}
  • Invoker
public class Button {
    private Command theCommand;
    public Button(Command theCommand) { //생성자로 원하는 기능의 Command를 받음
        setCommand(theCommand);
    }
    public void setCommand(Command newCommand) { //이 메소드를 통해 기능을 변경할 수 있음
        this.theCommand = newCommand;
    }
    public void pressed() {
        theCommand.execute();
    }
}
  • Receiver
public class Client {
    public static void main(String[] args) {
        Lamp lamp = new Lamp();
        Command lampOnCommand = new LampOnCommand(lamp);
        Command lampOffCommand = new LampOffCommand(lamp);
        Alarm alarm = new Alarm();
        Command alarmStartCommand = new AlarmStartCommand(alarm);

        Button button1 = new Button(lampOnCommand); // 램프 키는 Command 설정
        button1.pressed(); // 램프 키는 기능 수행
        button1.setCommand(lampOffCommand);  //램프 끄는 Command로 변경
        button1.pressed();  //램프 끄는 기능 수행
        button1.setCommand(alarmStartCommand);  // 알람 Command로 변경
        button1.pressed();  //알람 기능 수행
    }
}

# 커맨드 패턴의 장점은 무엇인가요?

  • 기존의 코드를 변경하지 않고 새로운 커맨드를 추가할 수 있다. (OCP)
  • 실행될 기능이 변경되어도 호출자(Invoker) 클래스를 수정할 필요가 없다.

# 커맨드 패턴의 단점은 무엇인가요?

  • ConcreteCommand가 많아지면 코드가 복잡해질 수 있다.

# 참고자료

출처: 정인상, 채홍석.JAVA 객체지향 디자인 패턴.서울:한빛미디어,2019.
출처: https://kingchan223.tistory.com/305?category=894627 [어제보다 오늘 더]