들어가기에 앞서
https://www.patterns.dev/vanilla/command-pattern 에 있는 설명과 예시를 바탕으로 하여 관련 주제에 대해 이제까지 공부한 내용을 개인적으로 정리한 포스팅임을 밝힙니다.

- 명령을 처리하는 객체를 통해 메소드와 실행되는 동작의 결합도를 낮출 수 있다.
- 특정 작업을 실행하는 객체과 메소드를 호출하는 객체를 분리할 수 있다.
- 주요 세 가지 클래스: invoker, receiver, command
- invoker : command를 생성하고 실행하는 역할
나중에 실행할 수 있도록 명령을 대기열(queue)에 넣거나 이미 실행된 명령을 실행 취소하는 데 사용할 수도 있다. - receiver : 특정 command를 수신하고 처리
애플리케이션의 다른 클래스나 메소드에 위임하여 수행할 수 있다. - command : void를 반환하는 매개변수가 없는 메소드를 포함하는 인터페이스 (또는 추상 클래스)
입력 및 출력 매개변수의 데이터 유형과 같이 command를 실행하는데 필요한 모든 매개변수도 포함된다.
- invoker : command를 생성하고 실행하는 역할
사용 예제) 온라인 음식 배달 플랫폼 개발
사용자는 주문, 주문 음식 확인, 주문 취소를 할 수 있다.
class OrderManaer() {
constructor() {
this.orders = []
}
placeOrder(order, id) {
this.orders.push(id);
return `You have successfully ordered ${order} (${id})`;
}
trackOrder(id) {
return `Your order ${id} will arrive in 20 minutes.`
}
cancelOrder(id) {
this.orders = this.orders.filter(order => order.id !== id);
return `You have canceled your order ${id}`
}
}
// Usage
const manager = new OrderManager();
manager.placeORder('Pad Thai', '1234');
manager.trackOrder('1234');
manager.cancelOrder('1234');
이렇게 manager의 메소드를 직접 사용해서 개발하다가 특정 메소드의 이름을 변경하거나 메소드의 기능을 변경해야 하는 경우가 생길 수 있다. 예를 들어 placeOrder 대신 addOrder 로 메소드 이름을 바꾼다면? 앱의 규모가 크면 까다로운 작업이 될 것이다.
위의 방식처럼 코드를 작성하는 대신 manager 객체로부터 메소드를 분리하고 각각의 명령을 처리하는 함수를 만들 수 있다.
1. OrderManager 클래스를 리팩토링한다.
placeOrder, trackOrder, cancelOrder 를 직접 구현하는 대신, execute 라는 하나의 메소드만 가진다. 이 메소드는 인자로 주어진 어떤 명령이든 실행할 수 있다.
class OrderManager {
constructor() {
this.orders = []
}
execute(command, ...args) {
return command.execute(this.orders, ...args)
}
}
2. 메소드를 3개의 Command 로 만든다.
class Command {
constructor(execute) {
this.execute = execute
}
}
function PlaceOrderCommand(order, id) {
return new Command(orders => {
orders.push(id);
return `You have successfully ordered ${order} (${id})`;
}
}
function TrackOrderCommand(id) {
return new Command(() => `Your order ${id} will arrive in 20 minutes.`);
}
function CancelOrderCommand(id) {
return new Command(orders => {
orders = orders.filter(order => order.id !== id);
return `You have canceled your order ${id}`
})
}
3. 사용부
const manager = new OrderManager();
manager.execute(new PlaceOrderCommand("Pad Thai", "1234"));
manager.execute(new TrackOrderCommand("1234"));
manager.execute(new CancelOrderCommand("1234"));
위의 예제에서 OrderManager가 명령을 만들고 실행하는 invoker의 역할을 맡고 있다. execute 메소드를 통해 명령을 실행하며, 명령 실행 시 필요한 추가적인 인자들을 받아서 전달한다. Command 클래스는 다양한 구체적인 명령들을 생성하기 위한 기반 클래스로 사용된다. 마지막으로 PlaceOrderCommand, TrackOrderCommand, CancelOrderCommand 함수가 receiver 역할을 한다.
장점
- 객체와 메소드를 분리할 수 있다.
- 객체와 메소드를 분리하면 수명이 지정된 명령을 만들거나, 명령들을 큐에 담아 특정한 시간대에 처리하는 게 가능해진다.
단점
- 커맨드 패턴을 쓸만한 상황이 딱히 많지 않고 종종 불필요한 코드가 만들어지곤 한다.
📚 참고자료
'배워서 남 주자' 카테고리의 다른 글
JSX에서 null, false, React.Fragment의 차이 (0) | 2024.04.20 |
---|---|
tanstack react-virtual을 사용하여 무한 스크롤 성능 최적화 하기 (1) | 2024.03.24 |
디자인 패턴 - Mediator/Middleware 패턴 (0) | 2024.01.08 |
디자인 패턴 - Observer 패턴 (0) | 2023.12.24 |
디자인 패턴 - Module 패턴 (0) | 2023.12.02 |