요청이나 작업을 실행 가능한 객체로 포장하는 패턴. 작업을 값처럼 저장·전달·대기열에 넣을 수 있게 되고, 되돌리기 같은 부가 연산을 붙일 수 있다.
interface Command { execute(): void; undo(): void }
class InsertText implements Command {
constructor(private doc: Doc, private pos: number, private text: string) {}
execute() { this.doc.insert(this.pos, this.text) }
undo() { this.doc.remove(this.pos, this.text.length) }
}
const history: Command[] = []
function run(cmd: Command) { cmd.execute(); history.push(cmd) }
function undo() { history.pop()?.undo() }- 편집기의 실행 취소·다시 실행, 작업 대기열, 실행 기록, 매크로가 전형적인 사용처다
- Redux의 액션처럼 "무엇을 할지"를 데이터로 표현하는 방식도 같은 계열이다(useReducer)
리팩터링으로 오가기
『리팩터링』에는 함수를 명령으로 바꾸기(Replace Function with Command, 1판의 "메서드를 메서드 객체로"(Replace Method with Method Object))와 그 반대가 짝으로 있다. 긴 함수의 지역 변수를 필드로 옮기면 함수를 여러 메서드로 쪼개기 쉬워진다. 하지만 유연성의 대가는 복잡성이다. 파울러는 일급 함수(First-Class Function)와 명령 중 고르라면 95%는 함수를 고르고, 함수로는 얻을 수 없는 기능(되돌리기 등)이 필요할 때만 명령을 쓴다고 한다. 로직이 단순한 명령 객체는 평범한 함수로 되돌린다.
이름은 비슷하지만 명령-질의 분리의 "명령"은 상태를 바꾸는 함수를 뜻하는 다른 개념이다. 행동 패턴 전체는 행동 패턴.
출처: Command — Refactoring.Guru · 『리팩터링 2판』 마틴 파울러 (원서 Refactoring, 2nd Edition) · Replace Function with Command · Replace Command with Function