라떼군 이야기


7 / 13강

커맨드: 요청을 객체로 만들기

버튼은 문서 편집의 세부 방법까지 알 필요가 없습니다. 실행할 요청을 객체로 만들면 버튼, 메뉴, 작업 기록에서 같은 요청을 사용할 수 있습니다. 커맨드 패턴은 누가 요청하는지와 누가 실제로 처리하는지를 분리합니다.

실행 전 상태를 기억합니다

class Document:
    def __init__(self, name):
        self.name = name

class Rename:
    def __init__(self, document, new_name):
        self.document = document
        self.new_name = new_name
        self.previous = None
    def execute(self):
        self.previous = self.document.name
        self.document.name = self.new_name
    def undo(self):
        if self.previous is None:
            raise ValueError("Execute first")
        self.document.name = self.previous
        self.previous = None

document = Document("old")
command = Rename(document, "new")
command.execute()
print(document.name)
command.undo()
print(document.name)

출력은 new, old입니다. Rename은 이름을 바꾸기 전 값을 기억하고, undo에서 그 값으로 되돌립니다. 이 작은 예제의 계약은 한 번 실행하고 그 직후 한 번 되돌리는 것입니다. 실행 전에 undo를 부르면 오류를 냅니다.

여러 단계의 되돌리기를 만들려면 실행한 커맨드를 순서대로 보관하는 기록이 필요합니다. 마지막 작업부터 되돌리는 스택과 잘 맞습니다. 다시 실행하기, 기록 용량, 다른 사용자의 동시 변경도 따로 설계해야 합니다.

모든 작업을 되돌릴 수 있는 것은 아닙니다

전송한 이메일을 메모리 값처럼 원상 복구할 수는 없습니다. 외부 작업은 취소 가능 여부를 확인하거나 반대 효과를 내는 별도의 보상 작업이 필요합니다. 커맨드 객체로 감쌌다는 사실만으로 트랜잭션이나 정확히 한 번 실행이 보장되지는 않습니다.

확인 문제

이름을 A에서 B, B에서 C로 바꿨습니다. 두 번 되돌릴 때 어떤 순서로 커맨드를 꺼내야 할까요?

해설 보기

B에서 C로 바꾼 커맨드를 먼저 되돌려 B로 만든 뒤, A에서 B로 바꾼 커맨드를 되돌립니다. 실행 순서의 역순입니다. 각 커맨드가 자기 실행 직전 상태를 기억해야 합니다.

제품 기획, 개발 파트너 찾으시나요? 개인, 팀, 기업 모두 환영. 문제 정의부터 출시까지 함께합니다.