[JAVA/디자인패턴] Command Pattern, 커맨드 패턴
Command Pattern
요구사항(요청, 명령)을 객체로 캡슐화시키는 디자인 패턴.
이를 이용해서 다른 요구사항을 지닌 클라이언트를 매개변수화 시킬 수 있고, 요구사항을 큐에 넣거나 로그로 남길 수 있으며 작업 취소(undo) 기능을 지원할 수도 있다.
Command: Receiver를 알고 있고, Receiver의 메소드를 호출, Receiver의 메소드에서 사용되는 파라미터들은 Command에 저장됨
Receiver: 실제 명령(command)을 수행한다.
Invoker: 요청을 받아서, 요청을 실행하기 위해 Command 인터페이스 연결, Command 가 실제 어떻게 실행되는지 모른다.
Client: 무엇을 요청할지 결정하고, 요청 Command를 Invoker에 넘긴다.
STEP 1 : 조명을 켜고 끄는 리모컨
조명을 키고 끌 수 있는 명령을 할 수 있는 리모컨을 만들어 보도록 하자.
조명은 실제 행위를 하는 Receiver가 될 것이다.
키고 끄는 명령은 ConcreteCommand이고, execute()를 통해 Receiver을 행동하게 할 것이다.
리모컨은 요청을 실행하는 Invoker의 역할을 할 것이다.
1. Command와 ConcreteCommand
public interface Command {
void execute();
}
// Light를 끄는 명령 클래스
public class TurnOffLightCommand implements Command {
private Light light;
public TurnOffLightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
// Light를 켜는 명령 클래스
public class TurnOnLightCommand implements Command {
private Light light;
public TurnOnLightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
명령을 나타내는 인터페이스 및 Concrete Class이다. execute() 메서드를 선언 및 구현하여 실제 객체(Receiver)에게 명령을 처리하도록 한다.
2. Receiver
// 실제 Light 객체 (Receiver)
public class Light {
private boolean isOn = false;
public void turnOn() {
isOn = true;
System.out.println("조명이 켜졌습니다.");
}
public void turnOff() {
isOn = false;
System.out.println("조명이 꺼졌습니다.");
}
public boolean isOn() {
return isOn;
}
}
명령의 실제 수행을 담당하는 객체로, ConcreteCommand에서 정의한 execute() 메서드를 통해 작동한다.
3. Invoker
// 리모컨(Invoker)
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
명령을 실행하는 주체. 명령을 실행하기 위해 Command 객체를 호출한다.
4. Client
public class Client {
public static void main(String[] args) {
Light light = new Light();
TurnOnLightCommand turnOnCommand = new TurnOnLightCommand(light);
TurnOffLightCommand turnOffCommand = new TurnOffLightCommand(light);
RemoteControl remoteControl = new RemoteControl();
// 버튼 누르면 조명 켜기
remoteControl.setCommand(turnOnCommand);
remoteControl.pressButton();
// 버튼 누르면 조명 끄기
remoteControl.setCommand(turnOffCommand);
remoteControl.pressButton();
}
}
클라이언트는 명령을 추가하고 실행하는데에만 관여한다. 명령의 구체적인 동작은 ConcreteCommand 클래스의 execute()에 의해 정의된다.
STEP 2 : Invoker에 여러개의 명령을 일괄 실행하도록 하기
// 리모컨(Invoker)
public class RemoteControl {
private List<Command> commands = new ArrayList<>();
public void setCommand(Command command) {
this.commands.add(command);
}
public void pressAllButton() {
for(Command command: commands){
command.execute();
}
}
}
public class Client {
public static void main(String[] args) {
Light light = new Light();
TurnOnLightCommand turnOnCommand = new TurnOnLightCommand(light);
TurnOffLightCommand turnOffCommand = new TurnOffLightCommand(light);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(turnOnCommand);
remoteControl.setCommand(turnOffCommand);
remoteControl.pressAllButton();
}
}
setCommand 메서드를 통해 명령을 추가하고, pressAllButtons 메서드를 통해 저장된 모든 명령을 실행하고 있다.
마찬가지로 클라이언트는 명령을 추가하고 실행하는데에만 관여한다. 명령의 구체적인 동작은 ConcreteCommand 클래스의 execute()에 의해 정의된다.
STEP 3 : Command Pattern에 Undo 기능 포함하기
앞에서 요구사항을 큐에 넣거나 로그로 남길 수 있으며 작업 취소(undo) 기능을 지원할 수도 있다고 하였다. 이번에는 실행한 명령을 취소할 수 있는 기능을 만들어보겠다.
public interface Command {
void execute();
// Undo 기능을 위한 메서드
void undo();
}
public class TurnOffLightCommand implements Command {
private Light light;
public TurnOffLightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn(); // Light를 켜는 것으로 간주하여 Undo
}
}
public class TurnOnLightCommand implements Command {
private Light light;
public TurnOnLightCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff(); // Light를 끄는 것으로 간주하여 Undo
}
}
Command에 작업을 취소할 때의 Receiver의 행위에 대해서 작성한 메서드를 추가해준다.
// 리모컨(Invoker)
public class RemoteControl {
private List<Command> commands = new ArrayList<>();
public void setCommand(Command command) {
this.commands.add(command);
}
public void pressAllButton() {
for(Command command: commands){
command.execute();
}
}
// Undo 기능 수행
public void undoLastCommand() {
if (!commands.isEmpty()) {
Command lastCommand = commands.get(commands.size() - 1);
lastCommand.undo();
commands.remove(commands.size() - 1);
}
}
}
Invoker에는 마지막 명령을 취소할 수 있는 기능을 지원하도록 메서드를 추가해주었다. 취소 시 Command의 undo 명령이 실행된다.
public class Client {
public static void main(String[] args) {
Light light = new Light();
TurnOnLightCommand turnOnCommand = new TurnOnLightCommand(light);
TurnOffLightCommand turnOffCommand = new TurnOffLightCommand(light);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(turnOnCommand);
remoteControl.setCommand(turnOffCommand);
remoteControl.pressAllButton();
remoteControl.undoLastCommand();
}
}
Client에서는 마지막 동작을 취소하고 싶으면 Invoker의 undoLastCommand()를 호출하게 되고, 이 때 마지막 Command에 해당하는 undo()명령이 실행될 것이고 실제 Reveiver은 취소하는 행동을 실행한다.
'CS > Design pattern' 카테고리의 다른 글
[JAVA/디자인패턴] Composite Pattern, 컴포지트 패턴 (0) | 2023.12.05 |
---|---|
[JAVA/디자인패턴] Template Method Pattern, 템플릿 메서드 패턴 (0) | 2023.12.05 |
[JAVA/디자인패턴] State Pattern, 상태 패턴 (2) | 2023.11.29 |
[JAVA/디자인패턴] Facade Pattern, 퍼사드 패턴 (0) | 2023.11.27 |
[JAVA/디자인패턴] Adapter Pattern, 어댑터 패턴 (2) | 2023.11.27 |