Command Pattern (커맨드 패턴)
Command Pattern
정의
command pattern을 이용하면 요구사항을 객체로 캡슐화 하여, 요구사항을 나중에 이용할 수 있도록 메소드 이름과 매개변수 등 요구사항에 필요한 정보를 집어넣을 수 있다. 요청내역을 큐에 저장하거나 로그를 기록할 수 있으며, 작업취소 기능도 있다.
구성
1. Client : Client는 ConcreteCommand를 생성하고 Receiver를 설정한다.
2. Receiver : 요구사항을 수행하기 위해 어떤 일을 처리하는 객체
3. Invoker : 명령이 있으며 execute() 메소드를 호출하여 커맨드 객체에게 특정 작업을 수행하도록 요청
4. Command : 모든 커맨드 객체가 구현할 Interface다. Receiver에게 시킬 모든 명령은 execute()메소드를 호출함으로써 수행되며, Receiver에게 특정 작업을 처리하라는 지시를 내린다.
5. ConcreteCommand : 특정 행동과 Receiver를 bind한다. Invoker에서 execute()메소드 호출을 통해 요청하고 ConcreteCommand 객체에서는 Receiver에 있는 메소드를 호출함으로써 작업을 처리한다.
구현방법
1. 기능Class(Receiver)들을 캡슐화 한다.
2. 기능Class를 외부에 작동할 수 있는 Command (interface or class)를 추상화 및 구현한다.
3. 기능실행class(Invoker)를 command 타입 객체로 구현한다.
4. 기능실행class(Invoker)에 setter method를 구현하여 Command 타입 object를 가져오도록 한다.
5. Invoker Object, Receiver Object, Command 타입의 Receiver Object를 parameter로 하는 Object들을 생성한다.
6. Invoker Object에서 Setter method를 호출하여 해당 commandAction Object를 등록하고, invoker에서 Command를 execute할 수 있는 method를 호출한다.
특징
request부와 execute부를 분리하고, undo, 보관, log생성이 가능하다.
장점
Receiver와 Command만 추가하면 dynamic하게 object 호출이 가능하다.
단점
Object 구성부가 추가되면 abstract부분부터 수정해야 한다.
Client 소스
public class RemoteLoader {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
CeilingFan ceilingFan = new CeilingFan("Living Room");
CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);
CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);
remoteControl.setCommand(2, ceilingFanHigh, ceilingFanOff);
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.undoButtonWasPushed();
remoteControl.onButtonWasPushed(2);
remoteControl.offButtonWasPushed(2);
remoteControl.undoButtonWasPushed();
}
}
Receiver 소스
/**
* Receiver (천장의 FAN을 작동시킨다)
*/
public class CeilingFan {
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 CeilingFan(String location) {
// TODO Auto-generated constructor stub
this.location=location;
speed=OFF;
}
public void high(){
speed = HIGH;
}
public void medium(){
speed = MEDIUM;
}
public void low(){
speed = LOW;
}
public void off(){
speed = OFF;
}
public int getSpeed(){
return speed;
}
}
Invoker
/**
* Invoker
*/
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl() {
// TODO Auto-generated constructor stub
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;
}
/**
* @param slot
* @param onCommand
* @param offCommand
* 리모컨의 각 slot에 command를 넣는다.
*/
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* @param slot
* slot의 ON button이 눌리면 그 slot의 OnCommand의 execute()메소드가 호출된다.
*/
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
@Override
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()+"\n");
}
return stringBuffer.toString();
}
public void undoButtonWasPushed() {
// TODO Auto-generated method stub
undoCommand.undo();
}
}
Command 소스 (fan의 속도를 높임)
/**
* 이 class는 fan 속도를 높이고, Receiver인 CeilingFan 사이를 bind한다.
*/
public class CeilingFanHighCommand implements Command{
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan) {
// TODO Auto-generated constructor stub
this.ceilingFan=ceilingFan;
}
/*
* Invoker에서 execute를 호출하면 fan 속도를 높인다.
* unDo를 구현하기 위해 prevSpeed에 속도 값을 저장해 둔다.
*/
@Override
public void execute() {
// TODO Auto-generated method stub
prevSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
/*
* prevSpeed를 기준으로 speed를 설정한다.
*/
@Override
public void undo() {
// TODO Auto-generated method stub
if(prevSpeed == CeilingFan.HIGH){
ceilingFan.high();
}else if (prevSpeed == CeilingFan.MEDIUM){
ceilingFan.medium();
}else if (prevSpeed == CeilingFan.LOW){
ceilingFan.low();
}else if (prevSpeed == CeilingFan.OFF){
ceilingFan.off();
}
}
}
Command소스 (Fan의 속도를 설정)
public class CeilingFanOffCommand implements Command{
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanOffCommand(CeilingFan ceilingFan) {
// TODO Auto-generated constructor stub
this.ceilingFan = ceilingFan;
}
@Override
public void execute() {
// TODO Auto-generated method stub
prevSpeed = ceilingFan.getSpeed();
ceilingFan.off();
}
@Override
public void undo() {
// TODO Auto-generated method stub
if(prevSpeed == CeilingFan.HIGH){
ceilingFan.high();
}else if(prevSpeed==CeilingFan.MEDIUM){
ceilingFan.medium();
}else if(prevSpeed==CeilingFan.LOW){
ceilingFan.low();
}else if(prevSpeed==CeilingFan.OFF){
ceilingFan.off();
}
}
}
Command
/**
* 이 class는 null object이다.
* return할 object는 없어도 client에서 null을 처리하지 않아도
* 되게 할 때 사용한다.
*/
public class NoCommand implements Command{
@Override
public void execute() {
// TODO Auto-generated method stub
}
@Override
public void undo() {
// TODO Auto-generated method stub
}
}
Interface
/**
* 모든 command에서 구현해야 하는 interface이다.
* 모든 command는 execute method를 통해서 호출되며,
* 이 method에서는 receiver에 특정 작업을 처리하게 한다.
*/
public interface Command {
public void execute();
public void undo();
}
구현
'SW > DesignPattern' 카테고리의 다른 글
Facade Pattern (퍼사드 패턴) (0) | 2017.09.07 |
---|---|
Adapter Pattern (어댑터 패턴) (0) | 2017.09.07 |
Singleton Pattern (싱글턴 패턴) (0) | 2017.09.05 |
Factory Pattern (팩토리 패턴) (0) | 2017.09.05 |
Decorator Pattern (데코레이터 패턴) (0) | 2017.09.05 |