Command Pattern?
- 커맨드 패턴(Command Pattern)은 Client가 보낸 요청을 객체로 캡슐화하여 이를 나중에 이용할 수 있도록 필요한 정보를 저장, 로깅, 취소할 수 있게 하는 패턴입니다.
* 한마디로, 요청을 객체로 감싸서 관리하겠다는 말입니다
* 예시를 통해서 한번 이해해볼까요?
리모컨을 디자인 해달라는 의뢰를 받았습니다. 리모컨에서는 여러 기능들이 있겠죠. 에어컨을 키거나, 선풍기를 키거나, 음악 플레이어를 재생하거나 등 여러 가지 기능이 있습니다.
하지만 리모컨의 입장에서는 내가 무엇을 수행해야하는지 알 필요가 있을까요?
그저 어떤 버튼을 눌렀고, 어떻게 실행될지 알기만 하면 됩니다.
그렇게 되면 정보의 은닉(캡슐화)이 가능해집니다!
구성 요소
- 커맨드 패턴에서는 다음과 같은 요소들이 필요합니다!
- 수신자(Receiver) : 행동을 담당하는 객체 = 기능을 수행
- 커맨드(Command) : 수신자의 정보 + 행동이 들어있는 객체
- 발동자(Invoker) : 커맨드를 저장하는 객체 -> 버튼이 어떤 수행을 할 것인지 결정
- 클라이언트(Client) : 커맨드 객체를 생성하고, 발동자를 통해 수신자에게 할 행동을 결정함
그럼 차근차근 한번 알아볼까요?
다음과 같은 인터페이스가 있다고 해봅시다.
package designpattern.command;
public interface Command {
String execute();
}
1. 커맨드 객체
행동을 수행하는 객체입니다!
그러면 행동을 만들어 볼까요?
package designpattern.command;
class Music {
String on() {
return "play Music!!";
}
String off() {
return "play off Music!!";
}
}
2. Music의 모든 행동이 담겨있는 수신자 클래스
간단하게, 음악을 키고 끄는 행위를 만들어 보았습니다!
그럼 커맨드 패턴의 말대로, 행위를 담당하는 객체를 만들어보겠습니다!
package designpattern.command;
class MusicOnCommand implements Command {
private final Music music;
MusicOnCommand(final Music music) {
this.music = music;
}
@Override
public String execute() {
return music.on();
}
}
package designpattern.command;
class MusicOffCommand implements Command {
private final Music music;
MusicOffCommand(final Music music) {
this.music = music;
}
@Override
public String execute() {
return music.off();
}
}
3. 커맨드 객체 생성
음악을 키는 MusicOnCommand 객체와
음악을 끄는 MusicOnCommand 객체를 만들었습니다!
수신자의 정보를 저장하고 해당 기능을 수행할 것입니다
package designpattern.command;
public class RemoteController {
private Command command;
public void setCommand(final Command command) {
this.command = command;
}
public String pressButton() {
return command.execute();
}
}
4. 발동자 객체 생성
커맨드에 대한 정보를 저장하고, 버튼을 누르는 리모컨을 제작해보았습니다.
리모컨의 입장에서는 그저 버튼을 누르면 기능이 수행되게 보입니다!
package designpattern.command;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class RemoteControllerTest {
private RemoteController controller;
@BeforeEach
void setUp() {
controller = new RemoteController();
}
@Test
void 리모컨에서_음악이_꺼지는지_테스트() {
/* Given */
MusicOffCommand musicOffCommand = new MusicOffCommand(new Music());
/* When */
controller.setCommand(musicOffCommand);
/* Then */
assertThat(controller.pressButton()).isEqualTo("play off Music!!");
}
@Test
void 리모컨에서_음악이_켜지는지_테스트() {
/* Given */
MusicOnCommand musicOnCommand = new MusicOnCommand(new Music());
/* When */
controller.setCommand(musicOnCommand);
/* Then */
assertThat(controller.pressButton()).isEqualTo("play Music!!");
}
}
5. 편의상 Client 객체를 Test Code로 하겠습니다
각각의 커맨드에 따른 객체가 들어갔을 때, 달리 수행하는 역할입니다!
위의 테스트 코드를 돌려보면
너무나 기분좋게 통과됩니다 ㅎㅎ
정리된 Diagram을 보실까요?
고찰
그럼, 커맨드 패턴의 장점과 단점이 무엇일까요?
* 장점
- 기존 Code를 수정하지 않고, 새 명령을 쉽게 추가할 수 있다
- 명령의 호출자와 수신자의 의존성을 없앱니다.
* 단점
- 명령에 대한 각각의 클래스는 너무나 많아질 것입니다
참고
Command Pattern
'Developer > Design Pattern' 카테고리의 다른 글
Bridge Pattern - 브릿지 패턴 (0) | 2020.01.05 |
---|---|
Composite Pattern - 컴포지트 패턴 (0) | 2019.11.06 |
Adapter Pattern - 어댑터 패턴 (0) | 2019.06.23 |
Factory Method Pattern - 팩토리 메서드 (0) | 2019.06.21 |
Abstract Factory Pattern - 추상팩토리 패턴 (0) | 2019.06.18 |