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

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

커맨드 패턴(Command Pattern)

  • 이벤트가 발생했을 때 실행될 기능이 다양하면서 변경이 필요한 경우 이벤트를 발생시키는 클래스의 변경없이 재사용하고자 할 때 사용하는 디자인 패턴
    • 커맨드 패턴은 실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자 클래스(Invoker)와 실제 기능을 실행하는 수신자 클래스(Receiver) 사이의 의존성을 제거한다. 따라서 실행될 기능의 변경에도 호출자 클래스를 수정없이 그대로 사용할 수 있도록 해준다.
  • 알고리즘군을 정의하고 알고리즘 각각을 클래스로 캡슐화해 교환해서 사용할 수 있게 만든 패턴

만능 버튼 만들기 example

  • 만능 버튼: 눌리면 특정 기능을 수행
  • 예) 램프를 켜는 버튼
  • Button 클래스: 버튼이 눌렸음을 인식하는 클래스
  • Lamp 클래스: 불을 켜는 기능을 제공
    image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Lamp {
	public void turnOn() {
		System.out.println("Lamp On");
	}
}

public class Button {
	private Lamp theLamp;
	public Button(Lamp theLamp) {
		this.theLamp = theLamp;
	}
	public void pressed() {
		theLamp.turnOn();
	}
}

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

문제점

  • 1) 버튼이 눌렸을 때 램프를 켜는 대신에 다른 기능을 수행하기 위해서는 어떤 변경 작업을 해야 되는가? 예를 들어 버튼이 눌리면 알람을 시작시키려면?
    • => Button 클래스의 pressed 메서드 수정이 필요함
    • => 기능 변경을 위해서 기존 소스 코드를 수정하므로 OCP를 위반
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Alarm {
	public void start() {
		System.out.println("Alarming...");
	}
}
public class Button {
	private Alarm theAlarm;	
	public Button(Alarm theAlarm) {
		this.theAlarm = theAlarm;
	}
	public void pressed() {
		theAlarm.start();
	}
}
public class Client {
	public static void main(String[] args) {
		Alarm alarm = new Alarm();		
		Button alarmButton = new Button(alarm);
		alarmButton.pressed();
	}
}
  • 2) 버튼이 눌렸을 때 수행되는 기능을 프로그램이 동작할 때 결정하기 위해서는? 예를 들어 버튼이 처음 눌렸을 때는 램프를 켜고, 두 번째 눌렸을 때는 알람을 동작시키려면?
    • => 기능의 변경 또는 새로운 기능의 추가 때마다 Button 클래스를 수정해야함
    • => 기존 소스 코드를 수정하므로 OCP를 위반
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Lamp {
	public void turnOn() { System.out.println("Lamp On"); }
}
public class Alarm {
	public void start() { System.out.println("Alarming..."); }
}
enum Mode { LAMP, ALARM};
public class Button {
	private Lamp theLamp;
	private Alarm theAlarm;
	private Mode theMode;
	public Button(Lamp theLamp, Alarm theAlarm) {
		this.theLamp = theLamp;
		this.theAlarm = theAlarm;
	}
	public void setMode(Mode mode) { this.theMode = mode; }
	public void pressed() {
		switch ( theMode ) {
			case LAMP: theLamp.turnOn(); break;
			case ALARM: theAlarm.start(); break;
		}
	}
}
public class Client {
	public static void main(String[] args) {
		Lamp lamp = new Lamp();
		Alarm alarm = new Alarm();		
		Button button = new Button(lamp, alarm);
		
		button.setMode(Mode.LAMP);
		button.pressed();
		
		button.setMode(Mode.ALARM);
		button.pressed();
	}
}

해결책

  • 버튼이 눌렸을 때 수행될 기능을 캡슐화
    • 버튼은 수행될 기능을 캡슐화된 객체로서 전달 받음
    • 버튼이 눌리면 전달 받은 객체를 호출함으로써 구체적 기능을 수행
      image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public interface Command {
	abstract public void execute();
}
public class Lamp {
	public void turnOn() { System.out.println("Lamp On"); }
}
public class LampOnCommand implements Command { // 램프를 켜는 기능의 캡슐화
	private Lamp theLamp;
	public LampOnCommand(Lamp theLamp) {
		this.theLamp = theLamp;
	}
	public void execute() { theLamp.turnOn(); }
}
public class Alarm {
	public void start() { System.out.println("Alarming..."); }
}
public class AlarmOnCommand implements Command { // 알람을 울리는 기능의 캡슐화
	private Alarm theAlarm;
	public AlarmOnCommand(Alarm theAlarm) {
		this.theAlarm = theAlarm;
	}
	public void execute() { theAlarm.start(); }
}

public class Button {
	private Command theCommand;

	public Button(Command theCommand) {
		setCommand(theCommand);
	}
	public void setCommand(Command newCommand) {
		this.theCommand = newCommand;
	}
	// 버튼이 눌리면 주어진 Command의 execute 메서드를 호출함
	public void pressed() { 
		theCommand.execute();	
	}
}
public class Client {
	public static void main(String[] args) {
		Lamp lamp = new Lamp();
		Command lampOnCommand = new LampOnCommand(lamp);
		
		Button button1 = new Button(lampOnCommand); // 램프를 켜는 기능을 설정함
		button1.pressed();
				
		Alarm alarm = new Alarm();
		Command alarmOnCommand = new AlarmOnCommand(alarm); // 알람을 울리는 기능을 설정함
		Button button2 = new Button(alarmOnCommand);
		button2.pressed();
		
		button2.setCommand(lampOnCommand); // 알람을 울리는 기능을 설정함
		button2.pressed();
	}
}

커맨드 패턴의 일반적인 형태

image

커맨드 패턴의 순차다이어그램

image

커맨드 패턴을 위에 적용한 클래스 다이어그램

image

출처

  • Java객체지향 디자인패턴(한빛미디어)
This post is licensed under CC BY 4.0 by the author.

[디자인패턴] 템플릿 메서드 패턴(Template Method Pattern)

[디자인패턴] 디자인 패턴 정리