
이 저작물은 크리에이티브 커먼즈 저작자표시-동일조건변경허락 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.
TV |
on() off() setInputChannel() setVolume() |
Light |
on() off() |
FaucetControl |
openValue() closeValue() |
식당은 어떤 것을 요구하는 객체와 그 요구를 받아들이고 처리하는 객체를 분리시키는 객체지향 디자인 패턴의 한 모델이다. 이것을 위의 리모컨 API와 비교해 보면 리모컨 버튼을 눌렸을 때 호출되는 코드와 특정 업체에서 제공한, 실제로 일을 처리하는 코드를 분리시키는 것이 가능하다. 버튼을 눌렸을 때 그 객체의 “orderUp()”같은 메소드가 호출되면서, 리모컨에서는 어떤 객체가 무슨일을 하는지 모르지만 불이 켜진다는 식으로 일이 처리가 가능할 것이다.
클라이언트 -> createCommandObject() -> 커맨드(execute()) -> 인보커(setCommand()) -> execute() -> 리시버(action1, action2)
인보커에서 커맨드 객체의 execute()를 호출하면 리시버에 있는 특정 행동을 하는 메소드가 호출 됩니다.
public interface Command {
public void execute();
}
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class Light{
public void on() {
System.out.println("light is on");
}
}
public class SimpleRemoteControl {
Command slot;
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
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();
}
}
TV |
up() down() stop() lightOn() lightOff() |
public class GarageDoor {
public void up() {
System.out.println("Garage Door is open");
}
public void down() {
System.out.println("Garage Door is close");
}
public void stop() {
System.out.println("Garage Door is stop");
}
public void lightOn() {
System.out.println("Garage Door is light on");
}
public void lightOff() {
System.out.println("Garage Door is light off");
}
}
public class GarageDoorOpenCommand implements Command {
GarageDoor garageDoor;
public GarageDoorOpenCommand(GarageDoor garageDoor) {
this.garageDoor = garageDoor;
}
@Override
public void execute() {
garageDoor.up();
}
}
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
GarageDoor garageDoor = new GarageDoor();
LightOnCommand lightOn = new LightOnCommand(light);
GarageDoorOpenCommand garageOpen = new GarageDoorOpenCommand(garageDoor);
remote.setCommand(lightOn);
remote.buttonWasPressed();
remote.setCommand(garageOpen);
remote.buttonWasPressed();
}
}
커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수도 있다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업취소 기능도 지원 가능하다.
커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구사항을 캡슐화한 것이다. 이렇게 하기 위해 행동과 리시버를 한 객체에 집어 넣고, execute()라는 메소드만 외부에 공개하는 방법을 사용한다. *외부에서 볼 때는 어떤 객체가 리시버 역할을 하는지, 리시버가 실제 어떤 일을 하는지 알 수 없다. *명령을 통해서 객체를 매개변수화하는 예도 볼수 있었다. 예를 들어 전등켜기를 했다가 차고 문 열기 명령을 로딩하기도 했다. *그리고 커맨드 패턴을 조금만 확장하면 메타 커맨드 패턴이라는 것도 사용이 가능한데 메타, 커맨드 패턴은 명령들로 이루어진 매크로를 만들어서 여러 개의 명령을 한 번에 실행할 수 있다.
package com.kws.command.remote;
import com.kws.command.Command;
import com.kws.command.NoCommand;
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
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;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("\n----- Remote Control ------ \n");
for (int i = 0; i < onCommands.length; i++) {
stringBuffer.append("[slot " + i + "]" + onCommands[i].getClass().getName() + " " + offCommands.getClass().getName() + "\n");
}
return stringBuffer.toString();
}
}
package com.kws.command;
import com.kws.command.command.GarageDoor;
import com.kws.command.command.GarageDoorCloseCommand;
import com.kws.command.command.GarageDoorOpenCommand;
import com.kws.command.command.Light;
import com.kws.command.command.LightOffCommand;
import com.kws.command.command.LightOnCommand;
import com.kws.command.command.Stereo;
import com.kws.command.command.StereoOffWithCDCommand;
import com.kws.command.command.StereoOnWithCDCommand;
import com.kws.command.remote.RemoteControl;
public class RemoteControlTest {
public static void main(String[] args) {
//기본테스트
// SimpleRemoteControl remote = new SimpleRemoteControl();
// Light light = new Light();
// GarageDoor garageDoor = new GarageDoor();
// LightOnCommand lightOn = new LightOnCommand(light);
// GarageDoorOpenCommand garageOpen = new GarageDoorOpenCommand(garageDoor);
//
// remote.setCommand(lightOn);
// remote.buttonWasPressed();
// remote.setCommand(garageOpen);
// remote.buttonWasPressed();
RemoteControl remoteControl = new RemoteControl();
Light livingRoomLight = new Light("Living Room");
Light kitchenLight = new Light("kitchen Room");
GarageDoor garageDoor = new GarageDoor("");
Stereo stereo = new Stereo("Living Room");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
LightOnCommand kitchenLightOn = new LightOnCommand(kitchenLight);
LightOffCommand kitchenLightOff = new LightOffCommand(kitchenLight);
GarageDoorOpenCommand garageDoorOpen = new GarageDoorOpenCommand(garageDoor);
GarageDoorCloseCommand garageDoorClose = new GarageDoorCloseCommand(garageDoor);
StereoOnWithCDCommand stereoOnWithCD = new StereoOnWithCDCommand(stereo);
StereoOffWithCDCommand stereoOffWithCD = new StereoOffWithCDCommand(stereo);
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
remoteControl.setCommand(2, garageDoorOpen, garageDoorClose);
remoteControl.setCommand(3, stereoOnWithCD, stereoOffWithCD);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
remoteControl.onButtonWasPushed(2);
remoteControl.offButtonWasPushed(2);
remoteControl.onButtonWasPushed(3);
remoteControl.offButtonWasPushed(3);
}
}
*NoCommand 객체는 일종의 널 객체이다. 딱히 리턴할 객체는 없지만 클라이언트 쪽에서 null을 처리하지 않아도 되도록 하고 싶을 때 널 객체를 활용하면 좋다. 리모컨에서는 execute()메소드가 호출됐을 때 아무 일도 하지 않지만 빈 자리를 채우기 위한 용도로 NoCommand라는 객체를 집어 넣어 두었다. 널 객체는 여러 디자인 패턴에서 유용하게 쓰인다. 널 객체를 일종의 디자인 패턴으로 분류하기도 한다.
###커맨드 패턴 활용