목차
개요
내용
요약
사용자가 다양한 요청을 할 수 있고 로깅을 하는 등의 추가 작업을 하기 위해서 커맨드를 객체로 감싸자
예시
GUI 프레임워크를 만든다고 생각해봅시다. 버튼 컴포넌트를 만들었을 때 사용자가 클릭했을 때 어떤 액션을 취해야할까요? 정답은 이 프레임워크를 이용해 애플리케이션을 만드는 사용자 마음이겠죠. 즉, 프레임워크 개발자는 버튼 컴포넌트를 만들 때 버튼을 클릭했을 시 어떤 액션을 취해야하는지 모릅니다. 이 때 사용할 수 있는 방법이 커맨드 패턴입니다. 커맨드 패턴에선 커맨드라는 인터페이스를 가집니다. 이 인터페이스는 인자를 받지 않는 execute() 라는 메소드 하나를 가집니다. 위의 예제에선 버튼 객체가 이 커맨드 인터페이스를 구현한 concrete class를 멤버로 가집니다. 그리고 버튼이 클릭되었을 때 command.execute()를 호출하게 되죠. 아래 구조를 참고해주세요.
이렇게 된다면 연산을 하는 객체와 연산을 위한 정보를 가지고 있는 객체를 디커플링할 수 있습니다. 즉, 설계에 유연함이 생기게 됩니다. UI 프론트엔드 개발을 해보신 분이라면 자바스크립트 혹은 리액트 등이 클릭 이벤트를 다루는 방식을 아실겁니다. 콜백이라 불리는 함수를 넘겨주는 방식이죠. 커맨드 패턴은 이런 콜백에 대한 객체 지향적 대체제라고 할 수 있습니다.
구조
위 예제의 PastCommand를 생각해본다면 Invoker는 버튼, Receiver는 document가 될 수 있을 것입니다. 버튼을 클릭하면 복사된 내용이 document에 붙여넣기된다는 가정 하에 말이죠.
이야깃거리
언제 써야 할까?
- 다양한 연산을 유연하게 받고 싶을 때
- 요청을 나중에 실행하고 싶을 때. 최초 요청과 커맨드 객체는 다른 수명을 가집니다. 커맨드 객체를 다른 곳에 넣어둔 후 나중에 처리할 수 있습니다.
- 작업을 되돌릴 수 있어야 할 때. 커맨드 객체는 해당 명령의 상태를 가지고 있습니다. 즉, 커맨드 객체를 history 배열 등에 저장해둔다면 되돌리기가 편해집니다.
장단점
- (Pros) 연산을 호출하는 객체와 연산을 수행하는 객체를 디커플링할 수 있습니다.
- (Pros) 여러 커맨드를 합쳐서 복합 커맨드를 만들 수 있습니다.
- (Pros) 새로운 커맨드를 만들기 편합니다.,
- (Cons) 새로운 커맨드를 만들기 위해 계속 클래스를 만들어야 하므로 클래스의 수가 늘어나 번잡해질 수 있습니다.
Case Study
Case 1. Qt Menu
Python, C++ 등에서 쓸 수 있는 GUI 프레임워크인 qt는 메뉴 컴포넌트를 커맨드 패턴과 함께 제공하고 있습니다. 아래 예시는 모두 Qt Docs에서 발췌했습니다.
아래는 qt에서 메뉴를 만드는 코드입니다.
void MainWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAct);
fileMenu->addAction(openAct);
fileMenu->addAction(saveAct);
fileMenu->addAction(printAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);
editMenu = menuBar()->addMenu(tr("&Edit"));
editMenu->addAction(undoAct);
editMenu->addAction(redoAct);
editMenu->addSeparator();
editMenu->addAction(cutAct);
editMenu->addAction(copyAct);
editMenu->addAction(pasteAct);
editMenu->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct);
helpMenu->addAction(aboutQtAct);
}
여기서 메뉴들은 액션을 가지고 있습니다. 그리고 액션 클래스가 커맨드 패턴의 커맨드의 역할을 합니다. 아래 액션을 만드는 코드를 살펴봅시다.
void MainWindow::createActions()
{
newAct = new QAction(tr("&New"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
...
}
액션은 QAction 클래스의 객체입니다. QAction 클래스는 어떤 클래스일까요?
QAction은 유저 커맨드에 대한 추상화를 제공합니다.
Qt Menu에 대한 추가적인 설명이 필요하신 분을 위해 관련 설명이 잘 되어있는 링크를 첨부합니다.
출처
GoF Design Patterns
'Design Patterns' 카테고리의 다른 글
[Design Patterns] Iterator (0) | 2024.04.21 |
---|---|
[Design Patterns] Interpreter (0) | 2024.04.21 |
[Design Patterns] Chain of Responsibility (0) | 2024.03.31 |
[Design Patterns] Proxy (0) | 2024.03.31 |
[Design Patterns] Flyweight (1) | 2024.03.27 |