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 객체를 생성하고 매개 변수화할 수 있다. 즉, 명령을 쉽게 추가하거나 제거할 수 있으며 다른 요청으로 바꿀 수 있다.

명령 패턴 고려사항

명령 패턴은 로깅과 오류 처리를 구현할 때, 실행 대기열을 구현해야 할 경우에 유용하게 사용할 수 있다. 하지만 간단한 작업을 처리하는 경우나 실행할 명령이 고정적인 경우에는 사용하지 않는 것이 좋다.


명령 패턴 사용 시 장점

  1. 유연성과 확장성이 높아진다.
  2. 명령을 캡슐화하므로 디버깅이 쉬워진다.
  3. 실행 취소/다시 실행 같은 기능을 구현할수 있다.
  4. 간단한 명령들을 조합하여 복잡한 명령을 만들 수 있다.

명령 패턴 사용 시 단점

  1. 많은 Command 객체를 만들어야 하기 때문에 복잡성이 증가한다.
  2. 요청을 캡슐화하기 때문에 메모리 사용량이 증가할 수 있다.