소프트웨어 개발자/디자인패턴

[디자인패턴] 행동 패턴 - Command 패턴

yubi5050 2024. 5. 1. 17:51

행동(Behavioal) 패턴

객체들이나 클래스들 사이에 상호 작용이나 책임(역할)분배를 하는 방법에 대한 패턴. 여러 객체에 책임을 분배하고, 객체 간의 종속도 최소화 한다. 

 

주요 패턴으론 다음 사항들이 있다.

  • 옵저버(Observer) 
  • 커맨드(Command) <- 이번 글에서 다룰 내용
  • 상태(State) 
  • 전략(Strategy) 
  • 책임 연쇄(Chain_Of_Responsibility)
  • 반복자(Iterator)
  • 중재자(Mediator)
  • 반복자(Visitor)

 

Command (커맨드) 패턴이란

커맨드 패턴은 행동 패턴 중 하나로, 요청을 요청 정보를 가지고 있는 독립 실행 객체로 전환하는 패턴을 말한다. 이 변환을 통해, 요청을 메서드 파라미터로 넘기거나, 요청의 실행을 지연 또는 큐에 넣기, 되돌리기 기능 등을 지원 가능한 패턴한다.

 

등장 배경

텍스트 에디터 앱 제작시 (좌) 각 버튼들에 공통된 기능이 추가가 필요하다고 가정 (ex. 툴 바를 위한 핸들러)

 

좌:https://refactoring.guru/images/patterns/diagrams/command/problem2-2x.png 우:https://refactoring.guru/images/patterns/diagrams/command/problem3-en-2x.png

 

가장 간단한 방법은 버튼 종류 마다 중복된 코드를 작성하는 것.

 

해결책

비즈니스 로직과 UI 분리 처럼, GUI 객체가 요청을 직접 보내는 대신에, 요청 디테일을 추출하고, 호출되는 객체, 메서드 이름, 파라미터 리스트를 해당 요청을 발생 시키는 분리된 커맨드 클래스로 옮겨 진행.

 

https://refactoring.guru/images/patterns/diagrams/command/solution1-en-2x.png

 

즉 커맨드 객체는 여러 GUI 객체들과 비즈니스 로직 객체와의 연결을 제공한다.

GUI는 최종 비즈니스 로직 객체에 대해 알 필요가 없고, GUI 객체는 커맨드를 발생시키기만 함

 

구조

Command 객체

  • Command 인터페이스는 파라미터가 없는 execute()라는 단일 메서드를 가진다.
  • 객체화 된 ConcreteCommand1, ConcreteCommand2는 receiver(호출되는 객체), 파라미터 등을 참조(객체 변수화)한다.
  • 최종 command의 실행 결과를 Receiver에서 받는다.

 

Invoker 객체

  • ex) Button 등 이벤트를 발생 시키는 컴포넌트를 의미 할 수 있다.
  • 버튼에서 이벤트를 발생시 command의 execute()를 수행하는 역할이다.

 

클라이언트

  • 버튼에 커맨드를 연결해둔다. (액션 발생시 Invoker가 수행된다.)
  • 커맨드 수행시 파라미터를 통해 액션들이 수행된다.
  • 수행된 결과는 Receiver를 통해 받는다.

 

https://refactoring.guru/images/patterns/diagrams/command/structure-indexed-2x.png

 

 

요약하면, Client 가 Invoker에 의해 Command의 execute() 함수를 호출하고, Receiver 를 통해 돌려 받는것

Command는 어떤 요청(들)에 대한 대표 묶음. (execute() 실행시 내부적으로 복잡한 로직이 있을 수 있지만, 나름의 추상화)

 

활용

  • 커맨드를 통해 파일(ex. 로그)이나, DB에 직렬화 하여 저장하고 향후 복구도 가능
  • 실행을 지연하여 스케쥴링에 활용
  • 명령을 큐에 집어 넣어 스케쥴링하고 싶을 때
  • undo/redo 명령을 구현 (대표 방법 중 하나) 하고싶을 때도 활용 가능 
  • 간단한 명령 집합 등은 복잡한 객체 하나로 모을 수도 있음

 

장단점

장점

  • SRP : 명령을 수행하는 클래스와 명령을 불러오는 객체와 decoupling (UI와 비즈니스 로직의 분리 등)
  • OCP : 새로운 커맨드를 추가할 때, 기존의 코드를 수정하지 않아도 됨
  • undo/redo 구현 / 지연된 명령 실행  등 다양한 활용 가능

 

단점

  • sender와 receiver 사이의 새로운 Layer를 생성해야 해서 복잡해진다.

 

다른 패턴과의 관계 

vs 책임 연쇄

  • 동적으로 연결된 잠재적 리시버가 요청을 처리할 때 까지 순차적으료 요청을 넘긴다.

 

vs 중재자

  • sender와 receiver 사이의 직접 연결을 제거하고, 강제로 중재자 객체를 통해 간접 통신

 

vs 옵저버 패턴

  • 리시버가 동적으로, 들어오는 요청에 대해 구독과 구독취소를 가능

 

커맨드 패턴

  • sender와 receiver 상의 단방향 연결을 구축

 

with 메멘토 패턴

  • "undo"를 구현하는데 동시에 사용가능
  • command 패턴은 다양한 명령을 타겟 command 객체를 통해 수행하는 책임이 있는데 반해,
  • 메멘토는 명령이 실행되기 바로 직전 상태를 저장