[디자인패턴] 커맨드 패턴 (Command Pattern)

Coffee Time☕·2021년 6월 4일
0

디자인 패턴

목록 보기
9/13
post-thumbnail

커맨드 패턴이란

필요성

버튼을 누르면 불이 켜지는 램프가 있다. 버튼은 불이 켜지는 기능을 가진 것이다.
이를 프로그램으로 작성한다고 하면, 보통 아래와 같은 UML 다이어그램으로 설계하기 쉽다.

public class Button(){
	private Lamp theLamp; //Lamp를 참조하기 위함
	public Button(Lamp theLamp){ //생성자
    	this.theLamp = theLamp ; 
    }
   public Button pressed(){ //Button을 누르는 경우 Lamp를 turnOn
   	theLamp.turnOn(); 
   }
}

public class Lamp(){ 
	public void turnOn(){ 
    	System.out.println("Lamp On"); 
    }
}

public static void main(String [] args){ 
	Lamp lamp = new Lamp(); 
    	Button b = new Button(lamp); 
    
    	b.pressed(); 
} 

이와 같이 설계하는 경우 어떠한 문제점이 있을까?
Button이 다른 추가 기능을 가지게 되는 경우를 생각해보자. Button에서 pressed() 함수에서 어떠한 방식으로 기능 선택할 것인지에 대해서 작성한 부분이 추가로 수정되어야할 것이다. 이는 OCP의 원리에 위배될 것이다.

이러한 경우에 커맨드 패턴을 사용한다. 커맨드 클래스를 이용한다면 기능들을 묶어 간편하게 관리하고 확장이 가능하다.

커맨드 패턴은 실행될 다양한 기능을 캡슐화한다. 따라서 확장이 용이하며, 클래스 사이의 의존성을 제거하여 기능의 변경에도 클래스를 사용할 수 있도록 해준다.

커맨드 패턴의 UML 다이어그램과 특징

다음은 커맨드 패턴의 일반적인 구조이다.

Command 클래스는 기능에 대한 인터페이스이다. 이를 상속한 세부적인 클래스에 관한 것이 concreteCommand 클래스이다.
Invoker 클래스는 기능 실행을 invoke(요청) 하는 클래스이다.
Receiver는 요청을 받아 동작하는 클래스이다.

  • MacroCommand: command를 사용자가 정의해서 한번에 실행할 수 있는 방식. 다른 command의 객체들을 연속해서 실행할 수 있다. Command 클래스의 하위 클래스로 MacroCommand를 생성하여 aggregation으로 이용할 수 있다.
  • Undo 메커니즘: Undo는 실행 작업을 실행 이전으로 되돌리는 것을 의미한다. 지금까지 실행된 command 객체를 stack등을 이용하여 역순으로 undo 할 수 있다.

    aggregation을 이용하여 호출되었던 다른 command를 undo 할 수 있다. undo라고 한다면 memento 패턴이 떠오른다. 메멘토 패턴과의 차이점은 메멘토는 객체 내부의 state를 저장한다는 점이고 command 패턴은 실행시키는 명령 자체를 캡슐화한다는 점에서 차이가 있다.
  • command를 객체로 캡슐화한다.
  • 객체에 수행되어야하는 기능이 동적으로 설정되는 경우 커맨트 패턴을 이용하기에 적절하다. 가령, 런타임에 어떠한 기능을 수행해야할지 정해야할 때, 커맨트 객체로 이를 캡슐화하는 것이 좋다.

커맨드 패턴의 예시

앞서 예시를 들었던 버튼을 커맨드 패턴으로 다시 구성해보자.

Button 클래스는 command를 호출하므로 invoker에 해당한다. pressed 되면 command를 호출해야하므로 theCommand를 이용하여 참조한다.
Command 클래스는 인터페이스이다. LampOnCommand, LampOffCommand가 Command 클래스의 자식 클래스로, Lamp를 참조하여 불을 키고 끄는 기능을 수행하게 한다.
Lamp 클래스는 기능을 수행받기 때문에 Reciever 클래스에 해당한다.

0개의 댓글