커맨드 패턴을 알아보자
커맨드 패턴
커맨드 패턴(Command Pattern)
요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 이를 통해 요청을 저장하거나, 로그로 기록하거나, 작업 취소 기능을 사용할 수 있다.
즉 요청하는 객체와 요청을 수행하는 객체를 분리하고 싶다면 커맨드 페턴을 사용하면 된다.
커맨드 패턴 정의 및 클래스 다이어그램
커맨드 패턴을 사용하면 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다. 행동과 리시버(요청을 수행하는 객체)를 한 객체에 넣고 execute() 메서드 하나만 외부에 공개하는 방법을 사용한다.
클래스 그램은 다음과 같다.

Client : 클라이언트는 ConcreteCommand를 생성하고 Receiver를 설정한다.
Invoker : 인보커에는 명령이 들어있으며, execute() 메서드를 호출함으로써 커맨드 객체에게 특정 작업을 수행해 달라는 요구를 하게 된다.
Command : 모든 커멘드 객체에서 구현해야 하는 인터페이스이다. 모든 명령은 execute() 메서드 호출로 수행되며, 이 메서드는 리시버에 특정 작업을 처리하라는 지시를 전달한다. undo() 메서드는 작업 취소 과정을 수행한다.
Receiver : 리시버는 요구 사항을 수행할 때 어떤 일을 처리해야 하는지 알고 있는 객체이다.
ConcreteCommand : 특정 행동과 리시버를 연결해준다. 인보커에서 execute() 호출로 요청하면 ConcreteCommand 객체에서 리시버에 있는 메서드를 호출해서 작업을 처리한다.
위 다이어그램에 맞춰 코드로 구현해보도록 하겠다.
커맨드 패턴 구현
커맨드 인터페이스는 다음과 같다. 커맨드 객체는 모두 같은 인터페이스를 구현해야 한다.
public interface Command {
public void execute();
public void undo();
}
다음은 리시버 클래스이다. 요구 사항을 수행할 때 어떤 일을 하고 있는지 설정할 수 있다.
package command.a;
public class ReceiverA {
public void on() {
System.out.println("on receiver A");
}
public void off() {
System.out.println("off receiver A");
}
}
Command 인터페이스를 구현한 ConcreteOnCommandA와 ConcreteOnCommandB 클래스이다. Command 인터페이스를 상속받고 구현한다. onCommand는 undo 실행동작으로 리시버의 off를 설정한다. 반대로 offCommand는 undo 실행동작으로 리시버의 on을 설정한다. ConcreteOnCommandA와 ConcreteOnCommandB는 같은 리시버 인스턴스(receiverA)를 작동시켜야 한다.
public class ConcreteOnCommandA implements Command {
private ReceiverA receiverA;
public ConcreteOnCommandA(ReceiverA receiverA) {
this.receiverA = receiverA;
}
@Override
public void execute() {
receiverA.on();
}
@Override
public void undo() {
receiverA.off();
}
}
public class ConcreteOffCommandA implements Command {
private ReceiverA receiverA;
public ConcreteOffCommandA(ReceiverA receiverA) {
this.receiverA = receiverA;
}
@Override
public void execute() {
receiverA.off();
}
@Override
public void undo() {
receiverA.on();
}
}
인보커 클래스이다.
package command;
import java.util.List;
public class Invoker {
private Command[] onCommand;
private Command[] offCommand;
private Command undoCommand;
public Invoker() {
this.onCommand = new Command[10];
this.offCommand = new Command[10];
undoCommand = new NoCommand();
for(int i=0;i<10;++i) {
onCommand[i] = new NoCommand();
offCommand[i] = new NoCommand();
}
}
public void setCommand(int offset, Command onCommand, Command offCommand) {
this.onCommand[offset] = onCommand;
this.offCommand[offset] = offCommand;
}
public void onBtn(int offset) {
onCommand[offset].execute();
undoCommand = onCommand[offset];
}
public void offBtn(int offset) {
offCommand[offset].execute();
undoCommand = offCommand[offset];
}
public void undo() {
undoCommand.undo();
}
}
배열에 Command를 저장한다. setCommand() 메서드로 커맨드 객체를 배열에 저장, onBtn()으로 리시버 실행, offBtn()으로 리시버 취소, undo()로 이전 명령어를 실행할 수 있다.
인보커 입장에서는 어떤 객체 리시버 역할을 하는지, 그 리시버가 어떤 일을 하는지 알 수도, 알 필요도 없다. 그저 execute() 메서드를 호출하면 해당 요청이 실행되고 undo() 메서드를 호출하면 이전 명령어가 실행된다는 것만 알면 된다.
상태를 저장하는 리시버
다음은 상태를 저장하는 리시버를 코드로 구현해보겠다.
package command.b;
public class ReceiverB {
public static final int HIGH = 10;
public static final int MIDDLE = 5;
public static final int LOW = 1;
public static final int OFF = 0;
private int state;
public ReceiverB() {
state = OFF;
}
public void setHigh() {
state = HIGH;
System.out.println("receiver b state : " + state);
}
public void setMiddle() {
state = MIDDLE;
System.out.println("receiver b state : " + state);
}
public void setLow() {
state = LOW;
System.out.println("receiver b state : " + state);
}
public void setOff() {
state = OFF;
System.out.println("receiver b state : " + state);
}
public void on() {
setLow();
System.out.println("on receiver B, state : " + getState());
getState();
}
public void off() {
setOff();
System.out.println("off receiver B, state : " + getState());
}
public int getState() {
return state;
}
}
위 코드를 보면 ReceiverB는 ReceiverA와 같이 OFF를 갖고 있다. ReceiverA와 다른 점은 HIGH, MIDDLE, LOW 상태를 갖고 있고 ON일 때 LOW 상태에서 실행된다. HIGH, MIDDLE, LOW 값은 state에 저장한다. 모든 상태를 실행하는 ConcreteCommand 클래스를 만들어야 한다. 코드는 다음과 같다.
public class ConcreteOnCommandB implements Command {
private ReceiverB receiverB;
private int prevState;
public ConcreteOnCommandB(ReceiverB receiverB) {
prevState = receiverB.getState();
this.receiverB = receiverB;
}
@Override
public void execute() {
receiverB.on();
prevState = receiverB.getState();
}
@Override
public void undo() {
if(ReceiverB.HIGH == prevState) {
receiverB.setHigh();
}
else if(ReceiverB.MIDDLE == prevState) {
receiverB.setMiddle();
}
else if(ReceiverB.LOW == prevState) {
receiverB.setLow();
}
else if(ReceiverB.OFF == prevState) {
receiverB.setOff();
}
System.out.println(receiverB.getState());
}
}
public class ConcreteOffCommandB implements Command {
private ReceiverB receiverB;
private int prevState;
public ConcreteOffCommandB(ReceiverB receiverB) {
prevState = receiverB.getState();
this.receiverB = receiverB;
}
@Override
public void execute() {
receiverB.off();
prevState = receiverB.getState();
}
@Override
public void undo() {
if(ReceiverB.HIGH == prevState) {
receiverB.setHigh();
}
else if(ReceiverB.MIDDLE == prevState) {
receiverB.setMiddle();
}
else if(ReceiverB.LOW == prevState) {
receiverB.setLow();
}
else if(ReceiverB.OFF == prevState) {
receiverB.setOff();
}
}
}
public class ConcreteLOWCommandB implements Command {
private ReceiverB receiverB;
private int prevState;
public ConcreteLOWCommandB(ReceiverB receiverB) {
prevState = receiverB.getState();
this.receiverB = receiverB;
}
@Override
public void execute() {
receiverB.setLow();
prevState = receiverB.getState();
}
@Override
public void undo() {
if(ReceiverB.HIGH == prevState) {
receiverB.setHigh();
}
else if(ReceiverB.MIDDLE == prevState) {
receiverB.setMiddle();
}
else if(ReceiverB.LOW == prevState) {
receiverB.setLow();
}
else if(ReceiverB.OFF == prevState) {
receiverB.setOff();
}
}
}
public class ConcreteMIDDLECommandB implements Command {
private ReceiverB receiverB;
private int prevState;
public ConcreteMIDDLECommandB(ReceiverB receiverB) {
prevState = receiverB.getState();
this.receiverB = receiverB;
}
@Override
public void execute() {
receiverB.setMiddle();
prevState = receiverB.getState();
}
@Override
public void undo() {
if(ReceiverB.HIGH == prevState) {
receiverB.setHigh();
}
else if(ReceiverB.MIDDLE == prevState) {
receiverB.setMiddle();
}
else if(ReceiverB.LOW == prevState) {
receiverB.setLow();
}
else if(ReceiverB.OFF == prevState) {
receiverB.setOff();
}
}
}
public class ConcreteHIGHCommandB implements Command {
private ReceiverB receiverB;
private int prevState;
public ConcreteHIGHCommandB(ReceiverB receiverB) {
prevState = receiverB.getState();
this.receiverB = receiverB;
}
@Override
public void execute() {
receiverB.setHigh();
prevState = receiverB.getState();
}
@Override
public void undo() {
if(ReceiverB.HIGH == prevState) {
receiverB.setHigh();
}
else if(ReceiverB.MIDDLE == prevState) {
receiverB.setMiddle();
}
else if(ReceiverB.LOW == prevState) {
receiverB.setLow();
}
else if(ReceiverB.OFF == prevState) {
receiverB.setOff();
}
}
}
'디자인패턴' 카테고리의 다른 글
| [헤드퍼스트 디자인패턴] 반복자 패턴과 컴포지트 패턴 (0) | 2024.08.23 |
|---|---|
| [헤드퍼스트 디자인패턴] 템플릿메서드 패턴 (0) | 2024.08.20 |
| [헤드퍼스트 디자인패턴] 싱글턴 패턴 (0) | 2024.08.13 |
| [헤드퍼스트 디자인패턴] 팩토리 패턴 (0) | 2024.08.12 |
| [헤드퍼스트 디자인패턴] 데코레이터 패턴 (0) | 2024.08.06 |