커맨드 패턴 (command pattern)
JAVA/Design Pattern

커맨드 패턴 (command pattern)

반응형

커맨드 패턴은 이벤트가 발생되었을 때

실행될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스는 변경하지 않고 재사용하고자 할 때 유용하다.

다음 예를 살펴보자

자동차의 
시동버튼이 눌러졌을 때 시동이 켜지도록 설계해보자.



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
// 버튼 클래스
public class Button {
    private Engine engine;
    
    public Button(Engine engine) {
        this.engine = engine;
    }
    
    public void on() {
        engine.execute();
    }
    
}
 
// execute 인터페이스
public interface Execute {
    void execute();
}
 
// 엔진 
public class Engine implements Execute {
 
    @Override
    public void execute() {
        System.out.println("Engine Start");
    }
 
}
cs




하지만 이런 구조에서
버튼을 눌렀을 때, 시동이 아닌 램프를 키고 싶은 경우에는 다음과 같이 변경작업을 진행해야한다.



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 Button {
    private Engine engine;
    
    public Button(Engine engine) {
        this.engine = engine;
    }
    
    public void on() {
        engine.execute();
    }
    
}
 
// 엔진 
public class Lamp implements Execute {
 
    @Override
    public void execute() {
        System.out.println("Lamp On);
    }
}
cs



이런경우 동작이 변경될 때 
기존의 코드가 변경되면 안된다는 OCP를 위반하게 된다.

이를 다음과 같이 변경하여
이 문제를 해결하는 것이 command pattern이다.



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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 버튼 클래스 (동작이되는 Command 인터페이스를 가지고 있는다.)
public class Button {
    private Command executeEle;
    
    public Button(Command executeEle) {
        this.executeEle = executeEle;
    }
    
    public void setCommand(Command executeEle) {
        this.executeEle = executeEle;
    }
    
    public void press() {
        executeEle.execute();
    }
    
}
 
// command 인터페이스
public interface Command {
    void execute();
}
 
// 엔진 객체
public class Engine {
 
    public void on() {
        System.out.println("Engine Start");
    }
 
}
 
// 엔진의 실행동작을 가지고 있는 command
public class EngineOnCommand implements Command {
    private Engine engine;
    
    public EngineOnCommand(Engine engine) {
        this.engine = engine;
    }
    
    @Override
    public void execute() {
        this.engine.on();
    }
    
}
 
// Lamp
public class Lamp {
    
    public void on() {
        System.out.println("Lamp on");
    }
}
 
// Lamp를 실행시키는 command
public class LampOnCommand implements Command {
 
    private Lamp lamp;
    
    public LampOnCommand(Lamp lamp) {
        this.lamp = lamp;
    }
    
    @Override
    public void execute() {
        this.lamp.on();
    }
    
}
 
public class Main {
    public static void main(String[] args) {
        // 엔진 키기
        Button button = new Button(new EngineOnCommand(new Engine()));
        button.press();
        
        button.setCommand(new LampOnCommand(new Lamp()));
        button.press();
    }
 
}
cs





실행을 요구하는 Button클래스와 
각 동작을 구현하는 Command 클래스, 그 클래스 내부에 동작이되는 대상을 삽입한다.





한가지 적용 가능한 예를 보면
화면에 메뉴를 구성하는 MenuItem 객체를
공용해서 사용하는데 동작만 변경해서 사용하고 싶을 경우 

1. MenuItem 클래스에 command라는 인터페이스를 가지고 있게 설계한다.
2. 객체의 동작을 호출하는 클래스를 command를 구현하여 작성한다.
3. 2번의 클래스는 각자 자신이 호출할 객체를 가지고 있다.

이렇게 설계하면 MenuItem 클래스에서 동작을 
변경하여 사용할 수 있다.


결론
기능의 실행을 요구하는 호출자(invoke)와 실제 기능을 수행하는 수진자(Receiver)사이의 의존성을 제거한다. 따라서 실행될 기능의 변경에도 호출자 클래스를 수정없이 사용할 수 있어야 한다.

결국 서로간의 의존성 및 커플링을 줄일 수 있도록 설계해야 한다.



반응형