- 프로그래밍이 가능한 리모컨에 7개의 슬롯이 있고, 각 슬롯에 필요한 프로그램을 연결
- 각 프로그램을 통해 특정 기능을 on/off 할 수 있음
- 프로그램 별로 on/off 기능을 추가하는 경우 모든 클래스에 대해 코드들을 추가해줘야 함
- 프로그램의 종류가 많아질수록 각 메서드를 각각 정의 및 추해줘야 하는 문제 발생
- 해당 클래스들을 관리하는 컨트롤러(리모컨)가 존재하는 경우 다양한 객체와 의존성을 깊게 갖기 때문에 변경에 어려움 발생
-
하나의 객체를 통해 여러 객체들에 명령해야할 때 사용되는 패턴
-
실행될 기능을 캡슐화
- 커맨드 객체는 명령을 받는 객체들에 대한 의존성 감소
- 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용
-
캡슐화된 method 호출을 로그 기록용으로 저장하거나 취소 기능을 구현하고 재사용 가능
고객 → 웨이트리스에게 주문 → 주문서 작성 → 주방장에게 주문 전달
- 웨이터 입장
- 어떤 주문인지 조차도 몰라도 된다.
- 누가 식사를 준비할지 몰라도 된다.
- 주방장 입장
- 주문서를 읽고 필요한 메뉴만 준비하면 된다.
- 누가 주문했는지 알 필요가 없다
- Invoker에서 단순히
execute()
를 호출makeBurger()
,makeShake()
와 같은 특정 행동은 Receiver에 정의된 대로 호출
Command
- 실행될 기능에 대한 인터페이스
- 실행될 기능을 execute 메서드로 선언함
ConcreteCommand
- 실제로 실행되는 기능을 구현
- 즉, Command라는 인터페이스를 구현함
Invoker
- 기능의 실행을 요청하는 호출자 클래스
Receiver
- ConcreteCommand에서 execute 메서드를 구현할 때 필요한 클래스
- 즉, ConcreteCommand의 기능을 실행하기 위해 사용하는 수신자 클래스
Structure
.
├── command
│ ├── command.go // Command interface
│ ├── light_off.go // Concrete command
│ ├── light_on.go // Concrete command
│ ├── stereo_off.go // Concrete command
│ └── stereo_on.go // Concrete command
│
├── receiver
│ ├── interface
│ │ ├── light.go // Receiver interface
│ │ └── stereo.go // Receiver interface
│ │
│ ├── kitchen.go // Concrete receiver
│ ├── living_room.go // Concrete receiver
│ └── stereo_player.go // Concrete receiver
│
├── invoker
│ └── remote_control.go // Invoker
│
└── main.go
Main
package main
func main() {
// 리모컨 객체 생성 - invoker(호출자)
remote := invoker.NewRemoteControl()
// 커맨드 기능을 실행하기 위한 객체 생성 - receiver(수신자)
livingRoom := receiver.NewLivingRoom()
// 실제로 실행되는 기능을 구현하는 객체 생성 - command
livingRoomLightOn := command.NewLightOnCommand(livingRoom)
livingRoomLightOff := command.NewLightOffCommand(livingRoom)
kitchen := receiver.NewKitchen()
kitchenLightOn := command.NewLightOnCommand(kitchen)
kitchenLightOff := command.NewLightOffCommand(kitchen)
stereoPlayer := receiver.NewStereoPlayer()
stereoPlayerOn := command.NewStereoOnWithCDCommand(stereoPlayer)
stereoPlayerOff := command.NewStereoOffWithCDCommand(stereoPlayer)
// 호출자(invoker)에 커맨드에 맵핑
_ = remote.SetOnCommand(0, livingRoomLightOn)
_ = remote.SetOffCommand(0, livingRoomLightOff)
_ = remote.SetOnCommand(1, kitchenLightOn)
_ = remote.SetOffCommand(1, kitchenLightOff)
_ = remote.SetOnCommand(2, stereoPlayerOn)
_ = remote.SetOffCommand(2, stereoPlayerOff)
// 호출자(invoker)에서 원하는 커맨드 실행
_ = remote.OnButtonWasPushed(0)
_ = remote.OnButtonWasPushed(2)
_ = remote.UndoButtonWasPushed()
}
- 특정 행동을 캡슐화한다는 점에서 command pattern 은 strategy pattern 과 유사하다.
- 사용 목적에 따라 command pattern을 적용해야 할지, strategy pattern을 적용해야할지를 결정할 수 있다.
- strategy pattern의 경우 특정 행위를 어떻게 구현하고, 수정하느냐에 집중을 하고, command pattern은 무엇을 실행하느냐에 집중을 한다. 실제로 어떻게 구현하는지에 대한 내용은 외부에서 작성하게 된다.
- 디테일한 내용은 아래 링크에 정리가 잘 되어 있습니다.
-
커맨드 패턴 예제
-
커맨드 패턴 설명
-
커맨트 패턴 vs 전략 패턴