커맨드 패턴
커맨드 패턴
👋 커맨드패턴이란
레스토랑에서 주문을 했다고 가정하자 손님은 항상 요리의 요리 방법을 알아야 하는가? 손님은 그저 웨이터에게 주문만 하면 된다. 요리 방법법을 알아야만 하는 건 요리사 뿐이다.
커맨드 패턴(Command pattern)이란 요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다.
- 커맨드 패턴에는 명령(
command
), 수신자(receiver
), 발동자(invoker
), 클라이언트(client
)의 네개의 용어가 항상 따른다. - 커맨드 객체는 수신자 객체를 가지고 있으며, 수신자의 메서드를 호출하고, 이에 수신자는 자신에게 정의된 메서드를 수행한다.
- 커맨드 객체는 별도로 발동자 객체에 전달되어 명령을 발동하게 한다.
- 발동자 객체는 필요에 따라 명령 발동에 대한 기록을 남길 수 있다. 한 발동자 객체에 다수의 커맨드 객체가 전달될 수 있다.
- 클라이언트 객체는 발동자 객체와 하나 이상의 커맨드 객체를 보유한다. 클라이언트 객체는 어느 시점에서 어떤 명령을 수행할지를 결정한다.
- 명령을 수행하려면, 클라이언트 객체는 발동자 객체로 커맨드 객체를 전달한다.
🎩 커맨드패턴이 필요한 이유
커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구사항을 캡슐화한다.
이렇게 하기 위해 행동과 리시버를 한 객체에 집어넣고 메소드 하나만 외부에 공개하는 방법을 사용한다.
🔧 커맨드패턴 클래스 다이어그램
🍔 식당을 예로들어보자
- 손님이 웨이터에게 주문을 한다.
- 웨이터가 고객의 주문을 주문서에 적는다.
- 웨이터는 주문서를 주방에 전달하여 주문을 요청한다.
- 주방장은 주문서에 적힌 주문대로 음식을 자신의 노하우로 만든다.
- 손님 ==
클라이언트
- 웨이터 ==
인보커 객체
- 주문서 ==
커맨드 객체
- 주방장 ==
리시버 객체
- 주문을 하는것 ==
setCommand()
- 주문을 요청하는것 ==
execute()
으로 볼수있다.
🎞 구현방법
1. 커맨드 인터페이스 구현
1
2
3
public interface Command {
public void execute();
}
2. 커맨드 구현 객체
1
2
3
4
5
6
7
8
9
10
11
12
13
public class LightOnCommand implements Command {
Light light; //이 Light 객체는 실제 불키는 방법을 알고있는 리시버 객체
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
}
3. 인보커
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SimpleRemoteControl {
Command slot;
public SimpleRemotecontrol() {
}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
4. 클라이언트
1
2
3
4
5
6
7
8
9
10
11
12
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
}
}
+ 매크로 커맨드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MacroCommand implements Command {
Command[] commands;
public MacroCommand(Command[] commands) { //커맨드의 묶음으로 메소드 한번으로 실행할 수 있다.
this.commands = commands;
}
public void execute() {
for (int o = 0; i < commands.length; i++) {
commands[i].execute();
}
}
}
🥊 커맨드 패턴의 활용방식
메시지 큐
- 큐 한 쪽 끝은 커멘드를 추가하고 다른 쪽 끝에는 커맨드를 처리하기 위한 스레드들이 대기.
- 각 스레드에서는 execute() 메소드를 호출하고 그 호출이 완료되고 나면 커맨드 객체를 보내고 새로운 커맨드
- 객체를 가져온다.
리퀘스트 로깅
- 자바에서는 이런 메소드를 객체 직렬화를 통해 구현할 수도 있지만, 직렬화와 관련된 제약 조건때문에 쉽지않을수 있다.
- 커맨더 패턴을 이용한다면, Command 인터페이스에 store(), load() 라는 메소드를 추가하여 기능을 지원 가능하다.
- 인보커 클래스가 Command1, Command2, Command3.. 각각 커멘드를 execute() 할때마다 각 Command 들은 store()를 실행시켜 디스크에 각각의 객체를을 저장한다.
- 컴퓨터가 다운되어 모든작업이 멈추고 이후 컴퓨터가 복구되었을때, 디스크로 부터 각각의 Command1, Command2 .. 작업완료 하지 못한 객체들을 로딩하고 다시 순서대로 작업을 처리한다.
This post is licensed under CC BY 4.0 by the author.