ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 행위패턴 > 커맨드 *
    기타/디자인패턴 2021. 2. 11. 10:14

     

     

    Invoker 와 Command 의 생명주기 같음

    ConcreateCommand 는 Command 의 일반화

    Client 는 ConcreteCommand 를 의존

    객체의 행위( 메서드)를 클래스로 만들어 캡슐화 하는 패턴

    어떤 객체(A)에서 다른 객체(B)의 메서드를 실행하기 위해 객체(B)를 의존하는 것을 방지

    또한 기능이 수정되거나 변경이 일어날 때 A 클래스 코드를 수정없이 기능에 대한 클래스를 정의하면 되므로 시스템이 확장성이 있으면서 유연해진다.

     

    Client 클래스 RemoteLoader 는 concreteCommand 를 생성하고 Receiver 를 생성한다.

    Receiver Light, Garage 는 요구 사항을 수행하기 위해 어떤 일을 해야 하는지 알고 있는 객체이다. 

    ConcreteCommand LightOnCommand, LightOffCommand 는 특정 행동과 리시버 사이를 연결해주는 역할이다.

    Invoker  RemoteControl 는 Command 객체를 갖고 있으며, execute 를 호출하면 리시버에 있는 메서드를 호출해 준다.

     

    Light, Garage 외에 선풍기, 오디오 같은 행위 객체들이 나중에 추가되더라도 

    RemoteControl 클래스는 수정할 필요가 없고 추가된 행위 객체들의 ConcreateCommand 객체만 추가해주면 된다.

    (OCP원칙)

    public class RemoteLoader { // Client 
        public static void main(String[] args) {
        	
            //Receiver 생성
            Light livingroom = new Light(); 
            
            //concreteCommand 를 생성하며, Reciever 를 설정
            LightOnCommand livingRoomLightOn = new LightOnCommand(livingroom); 
            LightOffCommand livingRoomLightOff = new LightOffCommand(livingroom);
           
            GarageDoor garageDoor = new GarageDoor();
            GarageDoorOpenCommand garageOpen = new GarageDoorOpenCommand(garageDoor);
            GarageDoorCloseCommand garageClose = new GarageDoorCloseCommand(garageDoor);
            
            //Invoker 생성
            RemoteControl rc = new RemoteControl();
            rc.setCommand(0, livingRoomLightOn, livingRoomLightOff);
            rc.setCommand(1, garageOpen, garageClose);
    
            System.out.println(rc);
            rc.onButtonWasPushed(0);
            rc.onButtonWasPushed(1);
            rc.offButtonWasPushed(0);
            rc.undoButtonWasPushed();       
        }
    }

     

    public class RemoteControl { //Invoker 클래스
        Command[] onCommands;
        Command[] offCommands;
        Command undoCommand; //마지막 작업을 undo 하기 위한 command
    
        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;
        }
    
        public void setCommand(int slot, Command onCommand, Command offCommand ){
            onCommands[slot] = onCommand;
            offCommands[slot] = offCommand;
        }
        
        public void onButtonWasPushed(int slot){
            onCommands[slot].execute();
            undoCommand = onCommands[slot];
        }
    
        public void offButtonWasPushed(int slot){
            offCommands[slot].execute();
            undoCommand = offCommands[slot];       
        }
    
        public void undoButtonWasPushed(){
            undoCommand.undo();
        }
    
    }

    * Receiver 객체의 필요성? Command 객체에서 execute 안에 넣으면 안되는지? 

    Receiver 있는 행동을 호출만 하는 "dummy" Command 객체를 만들 것인지

    아니면 Receiver에게 요구되는 구체 행동을 하는 "Smart" Command 객체를 만들 것인지에 대한 질문이다.

    이렇게 하면 Invoker (리모컨) 과 Receiver(리모컨 버튼이 하는 역할을 아는 객체) 의 분리되는 정도가 떨어지고 

    Receiver를 이용해 Command 객체를 매개변수화 하는 것도 어려워진다.

    LightOnCommand livingRoomLightOn = new LightOnCommand(livingroom); 
    rc.setCommand(0, livingRoomLightOn, livingRoomLightOff);

    public class Light { // Receiver 객체 
        public void on(){ 
            System.out.println("Light on");
        }
        public void off(){  
            System.out.println("Light off");
        }
    }
    public interface Command { // 주문 받는 웨이트리스와 같은 역할. 주문의 무엇인지 알 필요가 없다.
        public void execute();
        public void undo();
    }
    public class LightOnCommand implements Command { // ConcreteCommand 
    
        Light light;
        
        public LightOnCommand(Light light){
            this.light = light; 
        }
        @Override
        public void execute() {  //Receiver 의 메서드를 호출
            light.on(); 
            
        }
        @Override
        public void undo() {
            light.off();
            
        }      
    }

     

    버튼 하나로 여러개의 역할을 한 번에 처리하는 매크로 커맨드를 생성할 수도 있다.

    public class MacroCommandTest {
        public static void main(String[] args) {
            Light light = new Light();
            GarageDoor gd = new GarageDoor();
    
            LightOnCommand lo = new LightOnCommand(light);
            GarageDoorOpenCommand so = new GarageDoorOpenCommand(gd);
    
            LightOffCommand loff = new LightOffCommand(light);
            GarageDoorCloseCommand soff = new GarageDoorCloseCommand(gd);
    
            Command[] on = {lo,so};
            Command[] off = {loff,soff};
    
            MacroCommand mcon = new MacroCommand(on);
            MacroCommand mcoff = new MacroCommand(off);
    
            mcon.execute();
        }
    }
    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].undo();
            }       
        }
    }

     

    커맨드를 이용하면 리시버와 일련의 행동들(computation) 을 묶어 일급 객체의 형태로 전달할 수 있다. 

    이는 커맨드 객체가 생성되고 시간이 흘러도 여러 스레드에서 계속 그 객체를 이용하는 것이 가능해지고, 

    스케줄러, 스레드풀, 작업 큐와 같은 용도로 사용할 수 있다는 의미이다.

     

    혹은 애플리케이션의 모든 행동을 기록하다가 래플리케이션이 다운되었을 경우, 나중에 그 행동들을 다시 호출해서 복구할 수 있도록 할 때, 커맨드 패턴에 store , load 같은 메서드를 추가해 이런 기능을 지원할 수 있다.

    자바에서 직렬화를 통해 이 작업을 구현할 수 있지만 직렬화에 있는 제약조건들로 구현이 더 어려울 수 있다.

     

     

    출처: 

    Head First Design Patterns

    반응형

    '기타 > 디자인패턴' 카테고리의 다른 글

    행위패턴 > 옵저버 *  (0) 2021.02.12
    행위패턴 > 이터레이터 *  (0) 2021.02.11
    구조패턴 > 퍼싸드 *  (0) 2021.02.11
    구조패턴 > 데코레이터 *  (0) 2021.02.11
    구조패턴 > 컴포지트 *  (0) 2021.02.11
Designed by Tistory.