Table of Contents
[GoF 디자인 패턴] 명령 (Command) 패턴
명령 (Command) 패턴
행동(behavioral) 패턴 중 하나로, 어떤 작업을 수행하는 객체를 캡슐화하는 패턴이다.
작업을 요청하는 객체와 작업을 수행하는 객체가 서로 분리된 형태로, 요청을 대기시키거나 되돌릴 수 있는 연산을 제공한다.
명령 패턴 구성요소
- Command : 작업을 수행하기 위한 인터페이스 정의
- ConcreteCommand : Command 인터페이스를 구현하는 클래스. 실제 작업을 수행한다.
- Invoker : Command 객체 생성 및 작업 요청
- Receiver : 작업을 실제로 수행하는 객체
구현 예제
작업을 수행하기 위한 Command 인터페이스를 정의한다.
1public interface Command {
2 void execute();
3}
작업을 수행할 execute 메서드를 정의하였다.
Command 인터페이스를 구현하는 ConcreteCommand 클래스를 만든다.
1public class BuyCommand implements Command {
2 private final Order order;
3
4 public BuyCommand(Order order) {
5 this.order = order;
6 }
7
8 @Override
9 public void execute() {
10 order.buy();
11 }
12}
13
14public class CancelCommand implements Command {
15 private final Order order;
16
17 public CancelCommand(Order order) {
18 this.order = order;
19 }
20
21 @Override
22 public void execute() {
23 order.cancel();
24 }
25}
Command 인터페이스를 구현하는 BuyCommand와 CancelCommand 클래스를 만들었다.
각각의 클래스는 Order(Receiver) 객체를 생성자를 통해 주입받으며 실제 작업을 수행하는 Order(Receiver) 객체를 사용하여 Command 인터페이스의 execute() 메서드를 구현한다.
→ “작업을 요청하는 객체(Invoker)와 작업을 수행하는 객체(Receiver)를 분리하여 결합도를 낮춘다.”
작업을 수행하는 객체인 Receiver 클래스를 만든다.
1public class Order {
2
3 private final ProductType productType;
4 private final int price;
5
6 public Order(ProductType productType, int price) {
7 this.productType = productType;
8 this.price = price;
9 }
10
11 public void buy() {
12 System.out.printf("%s 을 %d 가격으로 구매합니다.%n", productType, price);
13 }
14
15 public void cancel() {
16 System.out.printf("%s 구매를 취소합니다.%n", productType);
17 }
18}
비즈니스 로직을 담고 있는 객체로, 모든 Command 클래스는 Receiver 객체의 메서드에게 실행을 위임한다. 여기서 Order 클래스는 buy(), cancel() 작업에 대한 구체적인 구현을 담당한다.
→ “객체 수정에 대한 유연성을 제공하며 확장 가능한 코드를 작성할 수 있다.”
마지막으로 작업을 요청하는 Invoker 클래스를 구현한다.
1public class OrderCommand {
2
3 private final List<Command> commandList = new ArrayList<>();
4
5 public void setCommand(Command command) {
6 commandList.add(command);
7 }
8
9 public void executeAll() {
10 for (Command c: commandList) {
11 c.execute();
12 }
13 }
14}
setCommand
메서드를 통해 Command 객체를 추가하고 executeAll
메서드를 통해 모든 Command 객체를 실행하는 역할을 수행한다.
→ “작업의 구체적인 내용을 알 필요가 없기 때문에 유연하고 확장 가능한 코드를 작성할 수 있다.”
명령 패턴을 사용하는 Client 코드이다.
1Order computerOrder = new Order(ProductType.COMPUTER, 1000000);
2Order phoneOrder = new Order(ProductType.PHONE, 500000);
3
4OrderCommand order1 = new OrderCommand();
5OrderCommand order2 = new OrderCommand();
6
7order1.setCommand(new BuyCommand(computerOrder));
8order1.setCommand(new CancelCommand(computerOrder));
9order1.executeAll();
10
11order2.setCommand(new BuyCommand(phoneOrder));
12order2.setCommand(new CancelCommand(phoneOrder));
13order2.executeAll();
Command 객체는 작업에 대한 요청을 캡슐화한다. 각 요청마다 별도의 Command 객체를 생성하고 매개 변수화할 수 있다. 즉, 명령을 쉽게 추가하거나 제거할 수 있으며 다른 요청으로 바꿀 수 있다.
명령 패턴 고려사항
명령 패턴은 로깅과 오류 처리를 구현할 때, 실행 대기열을 구현해야 할 경우에 유용하게 사용할 수 있다. 하지만 간단한 작업을 처리하는 경우나 실행할 명령이 고정적인 경우에는 사용하지 않는 것이 좋다.
명령 패턴 사용 시 장점
- 유연성과 확장성이 높아진다.
- 명령을 캡슐화하므로 디버깅이 쉬워진다.
- 실행 취소/다시 실행 같은 기능을 구현할수 있다.
- 간단한 명령들을 조합하여 복잡한 명령을 만들 수 있다.
명령 패턴 사용 시 단점
- 많은 Command 객체를 만들어야 하기 때문에 복잡성이 증가한다.
- 요청을 캡슐화하기 때문에 메모리 사용량이 증가할 수 있다.