본문 바로가기

IT Book Summary/디자인패턴

Command Pattern 커맨드 패턴, 호출 캡슐화

한 차원 높은 단계의 캡슐화

메소드 호출을 캡슐화하면 계산과정의 각 부분들을 결정화 시킬 수 있다.

 

 

홈 오토메이션 리모컨 구성

  • 일곱가지 프로그래밍 가능한 슬롯
  • 슬롯마다 on, off 버튼
  • 마지막 누른 버튼에 대한 명령을 취소하기위한 undo 버튼
  • 리모컨에서 제어해야하는 객체의 인터페이스등

커멘드 패턴을 스면 작업을 요청한 쪽과 처리하는 쪽을 분리시킬 수 있다.

커맨드 객체는 특정 객체에 대한 작업요청을 캡슐화시킴.

 


커맨드 패턴

 

command patterrn

 

클라이언트 는 커맨드 객체(리시버에 전달 할 일련의 행동으로 구성)를 생성.

creaatCommandObject()

 

커맨드 객체에는 행동과 리시버 Reciever에 대한 정보가 들어 있음.

 

커맨드 객체에서 제공하는 메소드는 execute() 하나로 행동을 캡슐화.

리시버의 특정 행동을 처리하기 위한 메소드를 호출하기 위한 메소드.

 

클라이언트에서 Invoker 객체의 setCommand() 메소드를 호출. 이때 커맨드 객체를 넘겨줌.

커맨드 객체가 쓰이기 전까지 인보커 객체에 보관.

 

인보커에서 커맨드 객체의 execute() 메소드를 호출하면, 리시버에 있는 특정 행동을 하는 메소드가 호출.

 

손님 - 클라이언트 객체
웨이트리스 - 인보커 객체
주문서  - 커맨드 객체
takeOrder() - setCommand()
orderUp() - execute()

첫번째 커맨드 객체

 

// 커맨드 객체는 모두 같은 인터페이스 구현
public interface Command {
  public void execute();
}

// 전등을 키는 커맨드 클래스 구현
public class LightOnCommand implements Command {
  Light light;
  
  public LightOnCommand(Light light) {
    this.light = light;
  }
  
  public void execute() {
    light.on(); 
  }
}

 

커맨드 객체 사용하기

 

public class SimpleRemoteControl {
  Command slot; // 커맨드를 집어넣을 슬롯.
  
  public SimpleRemoteControl() {}
  
  // 슬롯을 가지고 제어할 명령을 설정.
  // 클라이언트에서 이 메소드를 이용해 슬롯을 바꿈.
  public void setCommand(Command command) {
    solt = command;
  }
  
  // 버튼이 눌리면 호출되는 메소드. 슬롯에 연결된 커맨드 객체 execute() 호출
  public void buttonWasPressed() {
    slot.execute();
  }
}

 

리모컨 사용 테스트 클래스

//클라이언트에 해당
public class RemoteControlTes {
  public static void main(String[] args) {
    // 인보커 생성. 
    SimpleRemoteControl remote = new SimpleRemoteControl();
    Light light = new Light();
    // 리시버를 전달해주며 커맨드 객체 생성.
    LightOnCommand lightOn = new LightOnCommand(light);
    
    remote.setCommand(lightOn); // 커맨드 객체를 인보커에 전달.
    remote.buttonWasPressed();
  }
}

 


커맨드 패턴의 정의

Command Pattern Class Diagram

  • 클라이언트는 ConcreteCommand를 생성하고 Receiver를 설정함.
  • 리시버는 요구사항을 수행하기 위한 액션을 알고있는 객체.
  • ConcreatCommand는 특정행동과 리시버 사이를 연결해 줌. 
  • execute() 메소드에서는 리시버에 있는 메소드를 호출하여 요청된 작업을 수행
  • Command는 모든 커맨드 객체에서 구현해야하는 인터페이스
  • Invoker에는 명령이 들어있음. execute() 호춣여 커맨드 객체에게 특저작업 수행 요청.

슬롯에 명령 할당하기

 

리모컨

public class RemotoControl {
  Command[] onCommands;
  Command[] offCommands;
  Command undoCommand;
  
  public RemotoControl() {
    onCommand = new Command[7];
    offCommand = new Command[7];
    
    // 슬롯에 기본으로 널 객체를 둠. 빈자리를 채우기 위한 용도
    Command noCommand = new Command();
    for(int i = 0; i<7; i++) {
      onCommand[i] = noCommand;
      offCommand[i] = noCommand;
    }
    undoCommand = noCommand;
  }
  
  public void setCommand(int slot, Command onCommand, Command offCommand) {
  // 슬롯 번호와 슬롯에 저장할 ON OFF 명령을 인자로 전달
    onCommand[slot] = onCommand;
    offCommand[slot] = offCommand;
  }
  
  public void onButtonWasPushed(int slot) {
    onCommand[slot].execute();
    undoCommand = onCommands[slot];
  }
  
  public void offButtonWasPushed(int slot) {
    offCommand[slot].execute();
    undoCommand = offCommands[slot];
  }  
  
  public void undoButtonWasPushed() {
    undoCommand.undo();
  }
  
  public String toString() {
    StringBuffer stringBuff = new StringBuffer();
    stringBuff.append("\n----- Remote Control -----\n");
    for(int i = 0; i< onCommands.length; i++){
      stringBuff.append("[slot"+i+"] "+onCommand[i].getClass().getName()+
      "   " + offCommands[i].getClass().getName()+ "\n");
    }
    return stringBuff.toString();
  }
}

 

커맨드 클래스

public interface Command {
  public void execute();
  public void undo();
}

 

public class LightOffCommand implements Command {
  Light light;
  
  public LightOffCommand(Light light) {
    this.light = light;
  }
  
  public void execute() {
    light.off();
  }
  
  public void undo() {
    light.on();
  }
}

 

오디오를 켜고 끌때 쓸 커맨드 클래스

public class StereoOnWithCDCommand implements Command {
  Stereo stereo;
  
  public StereoOnWithCDCommand(Stereo stereo) {
    this.stereo = stereo;
  }
  
  public void execute() {
    stereo.on();
    stereo.setCD();
    stereo.setVolumn(11);
  }
  
  public void undo() {
    stereo.off();
  }
}

 

리모컨 테스트

public class RemoteLoader {
  public static void main(String[] args) {
    RemoteControl remonteControl = new RemoteControl();
    
    Light livingRoomLight = new Light("Living Room");
    Stereo stereo = new Stereo("Living Room");
    
    LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
    LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
    
    StereoOnWithCDCommand stereoOnWithCD = newStereoOnWithCDCommand(stereo);
    StereoOffWithCDCommand stereoOnWithCD = newStereoOffWithCDCommand(stereo);
    
    // 각 커맨드 로드. 추가
    remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
    remoteControl.setConmand(1, stereoOnWithCD, stereoOff);
    
    System.out.println(remoteControl);
    
    remoteControl.onButtonWasPushed(0);
    remoteControl.offButtonWasPushed(0);
    remoteControl.undoButtonWasPushed();
    
    remoteControl.onButtonWasPushed(1);
    remoteControl.offButtonWasPushed(1);
  }
}