커맨드 패턴(Command Pattern)은 요청을 객체로 캡슐화하여 요청의 실행, 취소(Undo), 저장을 가능하게 하는 디자인 패턴이다. 이 패턴을 활용하면 실행 요청을 별도의 객체로 분리할 수 있어 코드를 유연하고 확장 가능하게 만들 수 있다.
일반적인 방법으로 요청을 수행하면 특정 객체에 직접 메시지를 보내게 된다. 하지만 이렇게 하면 클라이언트와 수신자(Receiver) 간의 결합도가 높아져 유지보수가 어려워진다.
커맨드 패턴을 사용하면 클라이언트와 요청을 수행하는 객체(수신자) 사이에 커맨드 객체를 두어 결합도를 낮추고, 다양한 기능(예: 실행 취소, 큐에 저장 등)을 추가할 수 있다.
Client -> Invoker -> Command -> Receiver
// 1. Command 인터페이스 정의
public interface Command {
void execute();
void undo();
}
// 2. Receiver (실제 작업을 수행하는 클래스)
public class Light {
public void on() {
System.out.println("Light is ON");
}
public void off() {
System.out.println("Light is OFF");
}
}
// 3. ConcreteCommand (명령 객체)
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
// 4. Invoker (요청을 저장하고 실행하는 객체)
public class RemoteControl {
private Command slot;
public void setCommand(Command command) {
this.slot = command;
}
public void pressButton() {
slot.execute();
}
public void pressUndo() {
slot.undo();
}
}
// 5. Client (사용 예)
public class RemoteControlTest {
public static void main(String[] args) {
RemoteControl remote = new RemoteControl();
Light livingRoomLight = new Light();
Command lightOn = new LightOnCommand(livingRoomLight);
remote.setCommand(lightOn);
remote.pressButton(); // "Light is ON" 출력
remote.pressUndo(); // "Light is OFF" 출력
}
}
커맨드 패턴의 가장 큰 장점 중 하나는 실행 취소 기능을 쉽게 추가할 수 있다는 것이다.
// 실행 취소를 지원하는 리모컨
public class RemoteControlWithUndo {
private Command lastCommand;
public void setCommand(Command command) {
this.lastCommand = command;
}
public void pressButton() {
lastCommand.execute();
}
public void pressUndo() {
lastCommand.undo();
}
}
여러 개의 명령을 한꺼번에 실행하는 매크로 기능을 구현할 수도 있다.
public class MacroCommand implements Command {
private Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
}
}
@Override
public void undo() {
for (Command command : commands) {
command.undo();
}
}
}
undo() 메서드를 활용하여 실행 취소 구현