소프트웨어 개발/Design Pattern

⑪ 디자인 패턴(Design Pattern) - command

늘근이 2015. 9. 26. 09:03

Command Design Pattern Class Diagram.png


왜 굳이 메서드를 이용해서 쉽게 쉽게 가지않고, Command라는 인터페이스를 받아서 복잡하게 구성하는가?

클래스로 기능을 설정하면 이력등을 관리하기가 좋고, 나중에 필요한 기능 (ConcreteCommand)를 추가 하기가 쉽다. 단지 Command 만 상속받아서 구현을 하면 되니까.

커맨드 패턴은 기능을 실행하라고 하는 Sender와 요청을 받아들이는 Receiver가 완벽하게 분리되며 요청을 받아들이는 Receiver입장에서는 Sender의 인터페이스 따위는 알 필요가 없는 패턴이다.

위의 UML을 따라서 한번 구현한다. 코드는 시간이 없어서 위키피디아에 있는걸 그대로 따온다.

Command 인터페이스는 모든 ConcreteCommand에서 상속을 받아 구현하기때문에 굉장히 간단하게 execute() (혹은, 필요하다면 undo()) 만 선언되어있다.


public interface Command {
	void execute();
}


이 걸 상속받아서 불을 껐다 켰다 하는 ConcreteCommand들을 만들어 낸다.


public class TurnOnLightCommand implements Command {
	   private Light theLight;

	   public TurnOnLightCommand(Light light){
	        this.theLight=light;
	       }

	   public void execute(){
	      theLight.turnOn();
	   }
}


public class TurnOffLightCommand implements Command {
	   private Light theLight;

	   public TurnOffLightCommand(Light light){
	        this.theLight=light;
	       }

	   public void execute(){
	      theLight.turnOff();
	   }
}


리시버는, 실제로 Command에서 실행하는 리시버 클래스이다.


public class Light {
    public Light(){  }
    
    public void turnOn(){
       System.out.println("불 켜짐");
    }
    
    public void turnOff(){
       System.out.println("불 꺼짐");
    }
}


Invocker는 리모콘 혹은 스위치와 같다. 그냥 버튼만 누른다.


public class Switch {
    private Command upCommand;
    private Command downCommand;

    public Switch(Command upCmd,Command downCmd){
            this.upCommand=upCmd;
            this.downCommand=downCmd;
           }

    public void up(){
    	upCommand.execute();
    }
    
    public void down(){
    	downCommand.execute();
    }
}


Main 클래스는 아래와 같다.


public class Main {
	   public static void main(String[] args){
	       Light light=new Light();
	       Command up=new TurnOnLightCommand(light);
	       Command down=new TurnOffLightCommand(light);

	       Switch s=new Switch(up,down);
	       
	       s.up();
	       s.down();
	   }
}


이 귀찮은 패턴은 자바에서는 멀티쓰레드 프로그래밍 시 꼭 필요한 Runnable 인터페이스가 있다. Runnable 은 Command이고, Thread는 이를 구동시키는 Invoker역할을 한다. Run 에서 실행시키는 여타의 클래스가 Receiver라고 할수 있다.

어쨌든 우리는 run만을 구현해주어서 쓰레드로 돌게 하는데, 쓰레드 클래스를 구현하고 난리를 치지 않아도 되니 상당히 간편하며 자바 1.8부터는 람다식으로 쉽게 implement가 가능하니 코드도 확 줄어서 코딩을 할수 있겠다.