# 커맨드 (Command) 패턴
# 커맨드 패턴이란 무엇인가요?
실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자 클래스와 실제 기능을 실행하는 수신자 클래스 사이의 의존성을 제거해 주는 패턴입니다.
즉, 실행될 기능이 변경되어도 호출자 클래스의 변경없이 그대로 사용할 수 있도록 해주는 패턴입니다.
- 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 [어제보다 오늘 더]