핵심 의도

요청 내역을 객체로 캡슐화해서 객체를 서로 다른 요청 내역에 따라 매개변수화하거나 메모리에 보관할 수 있다.

적용 상황

요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다. 재실행이나 우선순위에 따라 명령어를 실행하거나 배치 실행도 가능하다.

솔루션의 구조와 각 요소의 역할

Untitled

객체에게 책임을 분할하기

요청을 보내는 책임과 그 요청을 처리하는 책임은 각각 Client와 Receiver에게 있다. 예를 들어 사용자가 선풍기를 켜고 싶다면 요청을 보내는 책임은 사용자에게, 선풍기를 켜는 요청을 처리하는 책임은 선풍기에게 있다. 그리고 사용자의 요청들을 중간에서 기록하고 전달하는 책임은 Invoker가 한다. 사용자가 선풍기 뿐만 아니라 부엌 불, 라디오 등을 키거나 끄고 싶다면 리모컨에 명령을 기록해두고 요청을 날릴 수 있는데 리모컨이 Invoker의 역할을 한다. 그리고 Invoker가 사용자의 요청을 Reciever에게 보내려면 통일된 인터페이스로 묶어주어야 하는데 이것이 Command 인터페이스이고, 이를 구현한 구체적인 커맨드 클래스는 Reciever에게 요청을 보내는 역할을 한다.

구현 포인트

  1. ConcreteCommand는 execute()에서 Receiver 객체에게 메시지를 요청해 동작 수행을 요청한다.
  2. ConcreteCommand가 undo() 동작을 수행할 때는 이전에 요청 내용과 상태를 저장해두었다가 반대 동작을 수행한다. 예를 들어 불을 켜는 동작에 대해 취소를 요청했다면 불을 끄는 동작을 실행하면 된다.
  3. Invoker의 생성자로 커맨드 클래스를 초기화할 때 빈 클래스는 NoCommand 객체로 초기화한다. NoCommand는 null 객체인데, 딱히 리턴할 객체가 없고 클라이언트가 null을 처리하지 않게 하고 싶을 때 활용한다.
  4. Invoker의 Command 클래스를 초기화할 때, ConcreteCommand 클래스 생성자를 하나씩 생성해 넘겨주기보다 람다식을 사용하면 더 깔끔하게 코드를 작성할 수 있다. 하지만 Command 인터페이스에 메소드가 하나일 때만 가능하다.