目次
the Definition of Command Pattern
커맨드 패턴의 정의는 다음과 같습니다.
커맨드 패턴 - 커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어 넣을 수도 있습니다. 또한 요청 내역을 큐에 저장하거 나 로그로 기록할 수도 있으며, 작업취소 기능도 지원 가능합니다.
하나씩 살펴볼까요? 커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구사항을 캡슐화한 것입니다. 이렇게 하기 위해서 행동과 리시버를 한 객체에 집어넣고, execute()라는 메소드 하나만, 외부에 공개하는 방법 을 씁니다. 이 메소드 호출에 의해서 리시버에서 일련의 작업이 처리됩니다. 외부에서 볼 때는 어떤 객체가 리시버 역할을 하는지, 그 리시버에서 실제로 어떤 일을 하는지 알 수 없습니다. 그냥 execute() 메소드를 호출하면 요구 사항이 처리된다는 것만 알 수 있을 뿐이죠.
이제 커맨드 패턴의 클래스 다이어그램을 살펴보도록 하겠습니다.
Class Diagram
Command class implements an undo function
커맨드에서 작업취소 기능을 지원하려면 execute()메소드하고 비슷한 undo()메소드가 있어야 합니다. execute()메소드에서 했던 것과 정 반대의 작업을 처리하면 되겠죠. 커맨드 클래스에 작업취소 기능을 추가하기 전에 우선 Command interface에 undo()메소드를 추가해야 됩니다.
public interface Command{ public void execute(); public void undo(); }
정말 간단하죠? 이제 Light 커맨드 클래스로 들어가서 undo() 메소드를 구현해 볼까요?
LightOnCommand부터 시작해 보죠. LightOnCommand()의 exeucte()메소드가 호출되어서 Light의 on()메소드가 호출되었다고 해 봅시다. 그러면 undo()메소드에서는 그 반대로 off()메소드를 호출해야 될 겁니다.
public class LightOnCommand implements Command{ Light light; public LightOnCommand(Light light){ this.light = light; } public void execute(){ light.on(); } public void undo(){ light.off(); } }
정말 식은 죽 먹기군요. 이제 LightOffCommand를 살펴볼까요? 이 클래스의 undo()메소드에서는 Light의 on()메소드만 호출하면 되겠군요.
public class LightOffCommand implements Command{ Light light; public LightOffCommand(Light light){ this.light = light; } public void execute(){ light.off(); } public void undo(){ light.on(); } }
정말 쉽죠? 아직 끝난 건 아닙니다. RemoteControl 클래스에 사용자가 마지막으로 누른 버튼을 기록하고, undo버튼이 눌렸을 때 필요한 작업을 처리하기 위한 코드를 추가해야 합니다.
Invoker Class
public class RemoteControlWithUndo{ Command[] onCommands; Command[] offCommands; Command undoCommand; public RemoteControlWithUndo(){ onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for(int i=0; i<7; i++){ onCommands[i] = noCommand; offCommands[i]= noCommand; } undoCommand = noCommand; } public void setCommand(int slot, Command onCommand, Command offCommand)[ onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot){ onCommands[slot].execute(); undoCommand = onCommands[slot]; } public void offButtonWasPushed(int slot){ offCommands[slot].execute(); undoCommand = offCommands[slot]; } public void undoButtonWasPushed(){ undoCommand.undo(); } public String toString(){ //toString 코드 } }
여기서 NoCommand는 아무것도 하지 않는 Null 객체1)입니다.
Client Class
undo 버튼을 테스트 할 수 있는 테스트용 클래스를 만들어 보겠습니다.
public class RemoteLoader{ public static void main(String[] args){ RemoteControlWithUndo remoteControl = new RemoteControlWithUndo(); Light livingRoomLight = new Light("Living Room"); LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight); LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight); remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff); remoteControl.onButtonWasPushed(0); remoteControl.offButtonWasPushed(0); System.out.println(remoteControl); remoteControl.undoButtonWasPushed(0); remoteControl.offButtonWasPushed(0); remoteControl.onButtonWasPushed(0); System.out.println(remoteControl); remoteControl.undoButtonWasPushed(0); } }