커맨드(Command) 패턴: 행위패턴
요청에 대한 모든 정보가 포함된 독립실행형 객체로 변환하는 디자인 패턴. 쉽게 말해 Client가 보낸 요청을 객체로 캡슐화한다는 의미이다.
이 변환은 다양한 요청들이 있는 메서드들을 인수화할 수 있도록 하며, 요청의 실행을 지연 또는 대기열에 넣을 수 있도록 하고, 또 실행 취소할 수 있는 작업을 지원할 수 있다.
어떤 객체(A)에서 다른 객체(B)의 메서드를 실행하려면 그 객체(B)를 참조하고 있어야 하는 의존성이 발생한다.
그러나 커맨드 패턴을 적용하면 의존성을 제거할 수 있다.
또한 기능이 수정되거나 변경이 일어날 때 A 클래스 코드를 수정없이 기능에 대한 클래스를 정의하면 되므로 시스템이 확장성이 있으면서 유연해진다.
- 발송자(Invoker) : 요청들을 시작하는 역할. 커맨드 객체에 대한 참조를 저장하기 위한 필드가 있어야한다. 발송자는 요청을 수신자에게 직접 보내는 대신 해당 커맨드를 작동시킨다.
- 커맨드(Command) : 커맨드를 실행하기 위한 단일 메서드만을 선언한다.
- 구상 커맨드(ConcreteCommand): 다양한 유형의 요청을 구현한다. 자체적으로 작업을 수행해서는 안되며, 비즈니스 논리 객체 중 하나에 호출을 전달해야한다. 그러나 코드를 단순화하기 위해 이러한 클래스들은 병합될 수 있다.
- 수신자(Receiver) : 일부 비즈니스 로직이 포함되어 있다. 거의 모든 객체는 수신자 역할을 할 수 있다. 대부분의 커맨드들은 요청이 수신자에게 전달되는 방법에 대한 세부 정보만 처리하는 반면 수신자 자체는 실제 작업을 수행한다.
- 클라이언트(Client) : 구상 커맨드 객체들을 만들고 설정한다. 수신자 인스턴스를 포함한 모든 요청 매개변수들을 커맨드의 생성자로 전달해야 한다.
이들의 역할을 간단하게 정리해보자면,
인보커: 명령어 클래스의 명령을 수행 할 수 있는 메소드 구현
커맨드: 인보커에 탑재하기 위해 리시버에 존재하는 기능을 활용하여 구현.
리시버: 실제 기능을 구현
클라이언트: 명령어 인스턴스 생성, 생성자로 리시버를 주입. 인보커 명령을 수행하면 주입된 커맨드의 메서드가 실행된다.
커맨드패턴의 장단점
장점
- 각각 캡슐화가 되어 결합도를 낮출 수 있다.
- 여러 객체에 직접 명령을 하지 않아도 됨으로 의존성을 가질 필요가 없다.
- 간단한 커맨드들의 집합을 복잡한 커맨드로 조합할 수 있다.
단점
- 인보커와 리시버 사이에 커맨드가 놓이기 때문에 코드가 복잡해진다.
- 리시버의 기능이 늘어남에 따라 커맨드도 같이 늘어나게된다.
왜 커맨드 패턴이 필요할까?
리모컨에 각각 이러한 기능이 있으며, 이런 구조로 설계를 했다 생각해보자. 추후 리모컨에는 다른 기능들이 추가된다.
기능을 추가함에 따라 리모컨은 각 기능들에 대해 의존도가 높아지며 여러 객체들을 가지고 있어야 되는 무거운 객체가 되버렸다. 기능을 추가하거나 수정하는 과정에서도 OCP를 위배할 것이다.
이럴 때 사용하는 패턴이 커맨드 패턴.
전략패턴과 커맨드패턴
어떻게 보면 전략패턴과 구조가 비슷하여 혼동이 올 수도 있다.
전략패턴은 ‘어떻게’ 라는 측면에 초점을 둔다. 하고자 하는 것은 이미 정해져 있고, 방법을 어떻게 할지에 대한 유연성을 고려한다.
커맨드 패턴은 ‘무엇을’에 초점을 둔다. 어떻게 할지에 대한 방법은 외부에서 주입하며, 그것을 실행하는 것이 중요하기 때문이다.
Command패턴 예제
Command interface
public interface CommandBasic {
public void execute();
}
Invoker
public class RemoteCtrl {
CommandBasic commandBasic;
public RemoteCtrl(CommandBasic commandBasic) {
this.commandBasic = commandBasic;
}
public void pressButton(){
commandBasic.execute();
}
}
ConcreteCommand
public class VolumeDownCommand implements CommandBasic{
Volume volume;
public VolumeDownCommand(Volume volume) {
this.volume = volume;
}
@Override
public void execute() {
volume.volumeDown();
}
}
public class VolumeUpCommand implements CommandBasic{
Volume volume;
public VolumeUpCommand(Volume volume) {
this.volume = volume;
}
@Override
public void execute() {
volume.volumeUp();
}
}
Receiver
public class Volume {
public void volumeUp(){
System.out.println("볼륨 키우기");
}
public void volumeDown(){
System.out.println("볼륨 낮추기");
}
}
Client
RemoteCtrl remoteCtrl = new RemoteCtrl(new VolumeUpCommand(new Volume()));
remoteCtrl.pressButton();
remoteCtrl = new RemoteCtrl(new VolumeDownCommand(new Volume()));
remoteCtrl.pressButton();
Reference
https://soojong.tistory.com/entry/디자인패턴-커맨드-패턴Command-Pattern
'OOP > Design Pattern' 카테고리의 다른 글
15. 이터레이터(iterator) 패턴 (0) | 2023.02.21 |
---|---|
14. 인터프리터(Interpreter) 패턴 (0) | 2023.02.21 |
12. 프록시(Proxy) 패턴 (0) | 2023.02.20 |
11. 플라이웨이트(Flyweight) 패턴 (0) | 2023.02.20 |
10. 퍼사드(Facade) 패턴 (0) | 2023.02.20 |