티스토리 뷰

1. 커맨드 패턴이란?

요구사항을 객체로 캡슐화하여, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수 있다.
요청 내역을 큐에 저장하거나, 로그로 기록할 수도 있으며, 작업취소 기능도 지원 가능하다.

 

구조

Command

- 기능을 수행하기 위한 인터페이스이다.

 

ConcreteCommand

- Receiver 객체와 액션끼리의 연결을 수행한다.

- execute()를 구현한다. -> 알맞는 Receiver 객체의 메소드를 호출한다.

 

Client

- ConcreteCommand 객체를 생성하여 그에 따른 Receiver를 연결한다.

 

Invoker

- Command에게 작업을 수행하도록 요청한다.

 

Receiver

- 실제로 작업을 처리하는 주체.

 

2. 동기

- 어떤 요청을 보내게 될 것인지, 누구에게 보내게 될 것인지 알 수 없을 때 사용하는 패턴이다.

- 요청 자체를 캡슐화시키기 때문에 요청을 하는 객체와 그 요청을 수행하는 객체가 분리가 된다. (decoupling)

 

장점:

1) 기존 코드를 변경하지 않고 새로운 커맨드를 만들 수 있다.

2) 수신자의 코드가 변경되어도 호출자의 코드는 변경되지 않는다.

3) 커맨드 객체를 로깅, DB에 저장, 네트워크로 전송하는 등 다양한 방법으로 활용 가능

 

단점:

1) 코드가 복잡하고 클래스가 많아진다.

 

3. 구현

구현 포인트:

1) 커맨드의 역할을 어디까지 설정해야 하는가?

- 가장 기본적인 형태는 receiver와 실제 수행을 연결해주는 역할

- 가장 복잡한 형태는 receiver에게 그 어느 것도 위임하지 않고 자체에서 모든 것을 구현하는 것.

- 이 사이에서 방향을 잡아야 한다.

 

2) undo / redo

 

샘플코드:

public interface Command {
    void execute();
    void undo();
}

 

public class Light {

    private boolean isOn;

    public void on() {
        System.out.println("불을 켭니다.");
        this.isOn = true;
    }

    public void off() {
        System.out.println("불을 끕니다.");
        this.isOn = false;
    }

    public boolean isOn() {
        return this.isOn;
    }
}

 

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() {
        new LightOffCommand(this.light).execute();
    }
}

 

public class LightOffCommand implements Command {

    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }

    @Override
    public void undo() {
        new LightOnCommand(this.light).execute();
    }
}

 

public class Button {

    private Stack<Command> commands = new Stack<>();

    public void press(Command command) {
        command.execute();
        commands.push(command);
    }

    public void undo() {
        if (!commands.isEmpty()) {
            Command command = commands.pop();
            command.undo();
        }
    }

    public static void main(String[] args) {
        Button button = new Button();
        button.press(new GameStartCommand(new Game()));
        button.press(new LightOnCommand(new Light()));
        button.undo();
        button.undo();
    }
}

 

4. 개인적인 포인트 - ExecutorService