커맨드: 요청을 객체로 만들기
버튼은 문서 편집의 세부 방법까지 알 필요가 없습니다. 실행할 요청을 객체로 만들면 버튼, 메뉴, 작업 기록에서 같은 요청을 사용할 수 있습니다. 커맨드 패턴은 누가 요청하는지와 누가 실제로 처리하는지를 분리합니다.
실행 전 상태를 기억합니다
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로 바꾼 커맨드를 되돌립니다. 실행 순서의 역순입니다. 각 커맨드가 자기 실행 직전 상태를 기억해야 합니다.