커맨드 패턴 (Command pattern)
정의
- 요청을 객체의 형태로 캡슐화하여(실행될 기능을 캡슐화함으로써) 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다
- 커맨드 패턴에는 명령(command), 수신자(receiver), 발동자(invoker), 클라이언트(client)의 네개의 용어가 항상 따른다.
다이어 그램
커맨드 패턴의 4가지 용어
-
명령(command) : 수신자(Receiver)의 정보 + 행동이 들어 있는 객체
-
수신자(receiver) : 행동을 하는 객체
-
발동자(invoker) : 커맨드를 저장함 수신자에게 행동을 전달
-
클라이언트(client) : 커맨드 객체를 생성, 발동자(Invoker)를 통해 수신자(Receiver)에게 할 행동을 전달함
커맨드 패턴의 동작 순서
-
클라이언트에서 커맨드 객체를 생성
-
인보커로 커맨드 객체를 저장
-
클라이언트에서 인보커를 통해 행동 요청을 전송
-
수신자가 행동을 함.
커맨드 패턴 사용전 - 1
- 구글홈이라고 “OK Google 히터 틀어줘” 라고 하면, 히터를 틀어주는 실제 구글 서비스가 있습니다.
- 구글홈을 사용하는 사용자를 Client 클래스
- 구글홈을 OKGoogle 클래스
- 히터를 Heater 클래스로 정의하도록 하겠습니다.
Code
1 |
|
Diagram
커맨드 패턴 사용전 - 2
- 그런데 OKGoogle에서 히터를 켜는 기능 말고, 램프를 켜는 기능을 추가하고 싶다
- 위와 같이 Lamp 클래스를 정의하고, OKGoogle 클래스에서 Lamp 객체를 참조하도록 해야 합니다.
- 물론 기존의 Heater 기능도 있어야 하구요.
Code
1 |
|
Diagram
문제점
- OKGoogle 에 추가적인 장치가 설정되었을때 객체 프로퍼티는 더욱 늘어나게 되고 talk() 메서드의 분기가 추가 된다.
- OCP 원칙에 어긋나게 된 코드가 된다.
커맨드 패턴을 사용후
- Command 인터페이스 를 정의한다.
Code
1 |
|
- Heater를 켜는 명령을 클래스화 하여, HeaterOnCommand 클래스를 정의하고, Heater 클래스는 그대로 히터를 켜는 powerOn() 메서드를 정의합니다.
Code
1 |
|
- 마찬가지로 Lamp를 켜는 명령을 클래스화 하여, LampOnCommand 클래스를 정의하고 Lamp 클래스는 그대로 램프를 켜는 turnOn() 메서드를 정의합니다.
Code
1 |
|
- OKGoogle 클래스의 talk() 메서드에서는 Command 인터페이스의 run() 메서드를 하여 명령을 실행합니다.
Code
1 |
|
- 마지막으로 OKGoogle을 사용하는 Client 클래스를 정의합니다.
Code
1 |
|
Diagram
장점과 단점
- 장점 :
- 커맨드 패턴을 활용하게 요청부와 동작부를 분리시켜주기 때문에 시스템의 결합도를 낮출 수 있으며, 각 객체들이 수정되어도 다른 객체가 영향을 받지 않습니다.
- 클라이언트와 INVOKER 클래스 간의 의존성이 제거 된다.
- 단점 : 리시버 및 리시버의 동작이 추가된다면 그 동작에 대한 클래스를 만들어야 하기 때문에, 다소 많은 잡다한 클래스들이 추가된다는 단점이 있습니다.