커맨드(Command) 패턴
커맨드 패턴은 요청을 객체로 캡슐화하여 요청자와 수행자 간의 결합을 느슨하게 만드는 행동 디자인 패턴입니다. 이 패턴을 사용하면 요청을 큐에 저장하거나 로그로 기록할 수 있고, 실행 취소(Undo) 기능도 쉽게 구현할 수 있습니다.
문제점: 요청과 수행 객체 간의 강한 결합
일반적인 코드에서는 **요청자(Invoker)**가 수행 객체(Receiver)에 대해 직접 호출을 수행합니다. 이 방식은 결합도가 높아 유지보수가 어려워지고, 동적인 요청 처리가 복잡해질 수 있습니다.
class Light {
public void turnOn() {
System.out.println("불 좀 켜줄래?");
}
public void turnOff() {
System.out.println("불 좀 꺼줄래?");
}
}
public class Main {
public static void main(String[] args) {
Light light = new Light();
light.turnOn();
light.turnOff();
}
}
문제점
- 결합도 증가: 클라이언트가 직접 Light 객체의 메서드를 호출합니다.
- 유연성 부족: 나중에 다른 명령을 추가하거나, 요청을 큐에 저장하기 어려워집니다.
- 실행 취소 기능을 구현하기가 복잡합니다.
해결 방법: 커맨드 패턴 적용
커맨드 패턴에서는 요청을 **명령 객체(Command)**로 캡슐화합니다. 요청자는 명령 객체를 통해 수행 객체를 호출하므로, 요청자와 수행 객체 간의 결합도를 줄이고 더 유연하게 요청을 처리할 수 있습니다.
커맨드 인터페이스 정의
interface Command {
void execute();
void undo(); // 실행 취소 기능을 위한 메서드
}
Receiver(수행 객체) 클래스 구현
class Light {
public void turnOn() {
System.out.println("불 좀 켜줄래?");
}
public void turnOff() {
System.out.println("불 좀 꺼줄래?");
}
}
구체적인 커맨드 클래스 구현
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn();
}
}
Invoker(요청자) 클래스 구현
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
클라이언트 코드
public class Main {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
// 불 켜기 명령 실행
remote.setCommand(lightOn);
remote.pressButton(); // 불 좀 켜줄래?
// 실행 취소 (불 끄기)
remote.pressUndo(); // 불 좀 꺼줄래?
// 불 끄기 명령 실행
remote.setCommand(lightOff);
remote.pressButton(); // 불 좀 꺼줄래?
// 실행 취소 (불 켜기)
remote.pressUndo(); // 불 좀 켜줄래?
}
}
커맨드 패턴의 동작 원리
- Command 객체는 수행할 작업을 캡슐화합니다.
- **Receiver(수행 객체)**는 실제 작업을 수행합니다.
- **Invoker(요청자)**는 명령 객체를 받아서 execute() 메서드를 호출합니다.
- 명령 객체는 실행 취소(Undo) 기능을 제공할 수도 있습니다.
커맨드 패턴의 장단점
장점
- 결합도 감소: 요청자와 수행 객체가 직접적으로 결합되지 않습니다.
- 유연한 요청 처리: 명령 객체를 큐에 저장하거나 실행 취소 기능을 쉽게 구현할 수 있습니다.
- 확장성 향상: 새로운 명령을 추가할 때 기존 코드를 수정할 필요가 없습니다.
단점
- 클래스 증가: 각 명령에 대해 별도의 클래스가 필요하므로 코드가 길어지고 클래스 수가 증가할 수 있습니다.
- 복잡성 증가: 작은 애플리케이션에서는 오히려 복잡성을 증가시킬 수 있습니다.
결론
커맨드 패턴은 요청을 객체로 캡슐화하여 결합도를 줄이고, 요청의 저장과 실행 취소 기능을 쉽게 구현할 수 있는 강력한 패턴입니다. 특히, 버튼 클릭 이벤트 처리, 트랜잭션 관리, 스마트 홈 시스템 등에서 자주 사용됩니다.
다만, 각 명령에 대해 별도의 클래스가 필요하기 때문에 코드가 복잡해질 수 있습니다. 따라서 규모가 큰 애플리케이션에서 주로 사용하며, 작은 시스템에서는 필요하지 않을 수 있습니다.