728x90
반응형
커맨드 패턴
커맨드 패턴을 사용하면 요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화할 수 있다.
이러면 요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.
리시버의 메서드 호출을 캡슐화 한다.
메서드 호출을 캡슐화하면 계산 과정의 부분을 결정화할 수 있기에 계산하는 코드를 호출한 객체는 그 일이 어떤 방법으로 처리되는지 신경 쓸 필요가 없다.
쉽게 이야기하면 다음과 같습니다.
Command 를 통해서 리시버의 특정 메서드를 캡슐화 하고,
인보커(호출자)는 리시버에 관계 없이(Command에서 캡슐화했기 때문) Command 객체의 메서드를 호출하면 됩니다.
즉, 커맨드 패턴은 호출을 캡슐화 합니다.
위 내용을 레스토랑의 종업원, 주방장의 예시로
코드를 사용해서 표현하면 다음과 같다.
Command
public interface Command {
void execute();
}
OrderCommand
public class OrderCommand implements Command {
private Chef chef;
public OrderCommand(Chef chef) {
this.chef = chef;
}
@Override
public void execute() {
chef.checkCookList();
chef.makeCook();
}
}
Employee
인보커 - 명령이 들어있으며 execute()
를 호출함으로써 커맨드 객체에게 특정 작업을 수행해 달라는 요구
public class Employee {
private Command command;
private List<String> list;
public void takeOrder(List<String> list) {
this.list = list;
System.out.println("종업원이 손님에게 주문을 받고 있습니다.");
for (String s : list) {
System.out.print(s+" ");
}
System.out.println("주문 받았습니다.");
}
public List<String> getList() {
return list;
}
public void setCommand(Command command) {
this.command = command;
}
public void orderUp() {
System.out.println("종업원이 주방장에게 주문서를 전달 합니다.");
command.execute();
}
}
Chef
리시버 - 요구사항을 수행할 때 어떤 일을 처리해야 하는지 알고 있는 객체
public class Chef {
List<String> list = new ArrayList<>();
public void setCookList(List<String> list) {
this.list = list;
}
public void checkCookList() {
setCookList(this.list);
System.out.println("주방장이 주문서를 확인했습니다.");
}
public void makeCook() {
System.out.println("주방장이 요리를 만들고 있습니다.");
for (String cookList : list) {
System.out.print(cookList+" ");
}
System.out.println("요리를 완성했습니다.");
}
}
Restaurant
public class Restaurant {
public static void main(String[] args) {
List<String> cookList = List.of("햄버거", "치킨", "파이"); //주문서
Employee employee = new Employee();
employee.takeOrder(cookList); //종업원이 주문서 수령
Chef chef = new Chef();
chef.setCookList(cookList);
Command orderCommand = new OrderCommand(chef); //주방장이 실행할 커맨드 객체 생성
employee.setCommand(orderCommand);
employee.orderUp(); //주방장에게 음식주문 요청
}
}
결과
종업원이 손님에게 주문을 받고 있습니다.
햄버거 치킨 파이 주문 받았습니다.
종업원이 주방장에게 주문서 내용을 알려줍니다.
주방장이 주문서를 확인했습니다.
주방장이 요리를 만들고 있습니다.
햄버거 치킨 파이 요리를 완성했습니다.
커맨드 패턴 구조
- Client
* `ConcreteCommand`를 생성하고 `Receiver`를 설정합니다.
- Invoker
- 인보커에는 명령이 들어 있으며,
execute()
메서드를 호출함으로써 커맨드 객체에게 특정 작업을 수행해 달라는 요구를 합니다.
- 인보커에는 명령이 들어 있으며,
- Receiver
- 리시버는 요구사항을 수행할 때 어떤 일을 처리해야 하는지 알고있는 객체입니다.
- Command
- 모든 커맨드 객체에서 구현해야 하는 인터페이스
- 모든 명령은
execute()
메서드 호출로 수행되며, 이 메서드는 리시버에 특정 작업을 처리하라는 지시를 전달 합니다. ConcreteCommand
- Command의 구현체로 특정 행동과 리시버를 연결해줍니다.
- 인보커에서
execute()
를 호출하면ConcreteCommand
객체에서 리시버에 있는 메서드를 호출해서 해당 작업을 처리합니다.
리모컨 애플리케이션 개발
커맨드 패턴을 사용해서 TV, 에어컨을 컨트롤 할 수 있는 리모컨 애플리케이션을 개발해보자!
여기에서 간단하게 undo()
메서드는 off()
메서드를 캡슐화 하였다.
Device
public interface Device {
void on();
void off();
}
Aircon
public class Aircon implements Device {
private String name;
public Aircon(String name) {
this.name = name;
}
@Override
public void on() {
System.out.println(name+"를 켭니다.");
}
@Override
public void off() {
System.out.println(name+"를 끕니다.");
}
}
TV
public class TV implements Device {
private String name;
public TV(String name) {
this.name = name;
}
@Override
public void on() {
System.out.println(name+"를 켭니다.");
}
@Override
public void off() {
System.out.println(name+"를 끕니다.");
}
}
Command
public interface Command {
void execute();
void undo();
}
TvCommand
public class TvCommand implements Command {
private Device device;
public TvCommand(Device device) {
this.device = device;
}
//캡슐화
@Override
public void execute() {
device.on();
}
//캡슐화
@Override
public void undo() {
device.off();
}
}
AirconCommand
private Device device;
public AirconCommand(Device device) {
this.device = device;
}
//캡슐화
@Override
public void execute() {
device.on();
}
//캡슐화
@Override
public void undo() {
device.off();
}
}
RemoteController
public class RemoteController {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void onButtonWasPush() {
command.execute();
}
public void offButtonWasPush() {
command.undo();
}
}
Client
public class Client {
public static void main(String[] args) {
//Receiver 생성
Device tv = new TV("티비");
Device aircon = new Aircon("에어컨");
//Command 생성
Command airconCommand = new AirconCommand(aircon);
Command tvCommand = new TvCommand(tv);
//Invoker 생성
RemoteController remoteController = new RemoteController();
//커맨드 의존성 추가
remoteController.setCommand(airconCommand);
remoteController.onButtonWasPush();
remoteController.offButtonWasPush();
//커맨드 의존성 추가
remoteController.setCommand(tvCommand);
remoteController.onButtonWasPush();
remoteController.offButtonWasPush();
}
}
결과
에어컨를 켭니다.
에어컨를 끕니다.
티비를 켭니다.
티비를 끕니다.
정리
- 커맨드 패턴을 사용하면 요청하는 객체와 요청을 수행하는 객체를 분리할 수 있다.
- 분리하는 과정의 중심에는 커맨드 객체가 있다.
- 커맨드 객체가 행동이 들어있는 리시버를 캡슐화 한다.
- 인보커는 커맨드 객체의
execute()
메서드를 호출한다. execute()
메서드가 마지막으로 호출되기 전의 상태로 되돌리는 작업 취소 메서드를 구현하면 커맨드 패턴으로 작업 취소 기능을 구현할 수 도 있다.- 매크로 커맨드는 커맨드를 확장해서 여러 개의 커맨드를 한 번에 호출할 수 있게 해주는 가장 간편한 방법이다.
- 스마트 커맨드 객체를 사용하면 요청을 스스로 처리한다.
- 커맨드 패턴을 활용해서 로그 및 트랜잭션 시스템을 구현할 수 있다.
728x90
반응형
'컴퓨터과학 > 0 + 소프트웨어 아키텍처(디자인 패턴)' 카테고리의 다른 글
[소프트웨어 아키텍처] 2. 전략 패턴(Strategy Pattern -java) (0) | 2022.10.25 |
---|---|
[소프트웨어 아키텍처] 6. 상태 패턴(State Pattern -java) (0) | 2022.10.24 |
[소프트웨어 아키텍처] 4. 옵저버 패턴(Observer Pattern -java) (0) | 2022.10.10 |
[소프트웨어 아키텍처] 1-3) 아키텍처 패턴 스타일 (2) | 2022.09.20 |
[소프트웨어 아키텍처] 1-2) 소프트웨어 아키텍처 문서화와 평가 (1) | 2022.09.20 |