Command Pattern

윤세영·2023년 7월 6일
0

DesignPattern

목록 보기
9/15

정의

  • command pattern을 이용하면 요구사항을 객체로 캡슐화 하여, 요구사항을 나중에 이용할 수 있도록 메소드 이름과 매개변수 등 요구사항에 필요한 정보를 집어넣을 수 있다. 요청내역을 큐에 저장하거나 로그를 기록할 수 있으며, 작업취소 기능도 있다.

구성

  • Client : Client는 ConcreteCommand를 생성하고 Receiver를 설정한다.
  • Receiver : 요구사항을 수행하기 위해 어떤 일을 처리하는 객체
  • Invoker : 명령이 있으며 execute() 메소드를 호출하여 커맨드 객체에게 특정 작업을 수행하도록 요청
  • Command : 모든 커맨드 객체가 구현할 Interface다. Receiver에게 시킬 모든 명령은 execute()메소드를 호출함으로써 수행되며, Receiver에게 특정 작업을 처리하라는 지시를 내린다.
  • ConcreteCommand : 특정 행동과 Receiver를 bind한다. Invoker에서 execute()메소드 호출을 통해 요청하고 ConcreteCommand 객체에서는 Receiver에 있는 메소드를 호출함으로써 작업을 처리한다.

구현방법

  • 기능Class(Receiver)들을 캡슐화 한다.
  • 기능Class를 외부에 작동할 수 있는 Command (interface or class)를 추상화 및 구현한다.
  • 기능실행class(Invoker)를 command 타입 객체로 구현한다.
  • 기능실행class(Invoker)에 setter method를 구현하여 Command 타입 object를 가져오도록 한다.
  • Invoker Object, Receiver Object, Command 타입의 Receiver Object를 parameter로 하는 Object들을 생성한다.
  • 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();
}

UML

profile
Frontend

0개의 댓글