커멘드에서 작업 취소 기능을 지원하려면 execute() 메서드와 비슷한 undo() 메서드가 있어야 한다. execute() 메서드에서 했던 것과 정반대의 작업을 처리하면 된다.
public interface Command {
public void execute();
public void undo();
}
LightOnCommand 클래스에 undo 메서드 구현
public class LightOnCommand implements Command{
// execute() 메서드가 호출되면 light 객체가 바로 그 요청에 대한 리시버(reciever)가 된다.
Light light;
// 생성자에서 이 커멘드 객체로 제어할 특정 전등(ex, 거실 전등)에 대한 정보가 전달됨.
public LightOnCommand(Light light){
this.light = light;
}
// execute() 메서드는 리시버 객체에 있는 on() 메서드를 호출
@Override
public void execute() {
light.on();
}
// undo() 메서드에서는 반대 행동인 off() 메서드를 호출하도록 한다.
@Override
public void undo() {
light.off();
}
}
LightOffCommand 클래스에 undo 메서드 구현
public class LightOffCommand implements Command{
Light light;
public LightOffCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
RemoteControl 클래스를 약간만 고쳐 작업취소(undo) 기능을 추가할 수 있다.
마지막으로 실행된 명령을 기록하기 위한 인스턴스 변수를 추가하고, UNDO 버튼을 누르면 기록해두었던 커멘드 객체 레퍼런스를 이용해서 undo() 호출수 있다.
public class RemoteControl {
// 하나 이상의 명령을 담을 수 있다.
Command[] onCommands; // on 계열 명령들
Command[] offCommands; // off 계역 명령들
Command undoCommand; // undo 버튼이 눌렸을 때를 대비하여 마지막으로 사용한 커멘드 객체를 집어넣을 변수
public RemoteControl(){
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;
}
// 명령을 등록할 때 사용하는 setCommand 메서드
public void setCommand(int slot, Command onCommand, Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonPushed(int slot){
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonPushed(int slot){
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
// 작업취소 기능 호출
public void undoButtonWasPushed(){
undoCommand.undo();
}
}
undo 기능을 테스트 해보자.
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("Living Room Light");
LightOnCommand lightOnCommand = new LightOnCommand(livingRoomLight);
LightOffCommand lightOffCommand = new LightOffCommand(livingRoomLight);
// on, off 커멘드 각 슬롯 0번에 거실 불 on, off 기능을 넣는다.(명령 셋팅)
remoteControl.setCommand(0, lightOnCommand, lightOffCommand);
remoteControl.onButtonPushed(0);
remoteControl.offButtonPushed(0);
// undo test
System.out.println("===== undo =====");
remoteControl.undoButtonWasPushed();
}
}
약간은 복잡한 CeilingFan 리시버 클래스
public class CelingFan {
// 상태
public static final int HIGH = 3;
public static final int MEDIUM = 2;
public static final int LOW = 1;
public static final int OFF = 0;
String location;
int speed;
public CelingFan(String location){
this.location = location;
}
public void high(){
speed = HIGH;
}
public void medium(){
speed = MEDIUM;
}
public void low(){
speed = LOW;
}
public int getSpeed(){
return speed;
}
}
이 선풍기 명령어에 작업취소 기능 추가하기 => 이전 상태로 되돌린다!
public class CeilingFanHighCommand implements Command{
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan){
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
prevSpeed = ceilingFan.getSpeed(); // 이전 상태를 저장
ceilingFan.high();
}
@Override
public void undo() {
if(prevSpeed == CeilingFan.HIGH)
ceilingFan.high();
else if(prevSpeed == CeilingFan.MEDIUM)
ceilingFan.medium();
else if(prevSpeed == CeilingFan.LOW)
ceilingFan.low();
else
ceilingFan.off();
}
}
public class MacroCommand implements Command{
Command[] commands;
public MacroCommand(Command[] commands){
this.commands = commands;
}
@Override
public void execute() {
for(int i=0; i<commands.length; i++){
// 리모콘에서 메크로를 실행시키면 각 커멘드들을 순서대로 실행시킴.
commands[i].execute();
}
}
@Override
public void undo() {
for(int i=0; i<commands.length; i++){
commands[i].execute();
}
}
}
public class RemoteLoader {
public static void main(String[] args) {
// ...
// ...
// 메크로 커멘드 test
Light light01 = new Light("Light 01");
GarageDoor garageDoor01 = new GarageDoor("Garage Door 01");
Stereo stereo01 = new Stereo("Stereo 01");
// on commands
LightOnCommand lightOn = new LightOnCommand(light01);
GarageDoorOpenCommand doorOpenCommand = new GarageDoorOpenCommand(garageDoor01);
StereoOnWithCDCommand stereoOnWithCDCommand = new StereoOnWithCDCommand(stereo01);
// off commands
LightOffCommand lightOff = new LightOffCommand(light01);
GarageDoorOffCommand doorOffCommand = new GarageDoorOffCommand(garageDoor01);
StereoOffWithCDCommand stereoOffWithCDCommand = new StereoOffWithCDCommand(stereo01);
Command[] onCommands = {lightOn, doorOpenCommand, stereoOnWithCDCommand};
Command[] offCommands = {lightOff, doorOffCommand, stereoOffWithCDCommand};
MacroCommand onMacro = new MacroCommand(onCommands);
MacroCommand offMacro = new MacroCommand(offCommands);
remoteControl.setCommand(0, onMacro, offMacro);
System.out.println("===== macro test");
remoteControl.onButtonPushed(0);
remoteControl.offButtonPushed(0);
}
}