-
[Design Pattern] 커맨드 패턴 (Command Pattern)공부/디자인 패턴 2021. 6. 12. 19:02
커맨드 패턴은 특정 객체에 대한 커맨드를 객체화 하여 커맨드 객체를 필요에 따라 처리하는 패턴입니다. 보통 주체 객체 → 대상 객체 와 같은 방식으로 호출한다면 대상 객체에 대한 액션은 주체 객체에서 메소드로 처리하는데 이 액션을 객체로 만들어 처리하는 방식입니다. 실행될 기능을 캡슐화함으로써 여러 기능을 실행할 수 있어 재사용성이 높습니다. 또한 기능이 수정되거나 변경이 일어났을 때, 주체 객체의 코드 수정 없이 기능에 대한 클래스만 정의하면 되어 확장성이 유연해집니다.
클래스 다이어그램
Invoker: 기능의 실행을 요청하는 호출자 클래스. Client의 요청을 받아 Receiver의 액션을 호출.
Command: 실행될 기능에 대한 인터페이스. 실행될 기능을 execute()로 정의
ConcreteCommand: 실행되는 기능을 구현하는 클래스. Receiver가 무엇을 처리해야 하는지(execute() 세부 구현) 정의
Receiver: ConcreteCommand에서 execute()를 구현할 때 필요한 클래스. ConcreteCommand의 기능을 실행하기 위해 사용하는 수신자 클래스. Client가 요청한 내용에 대해 액션만 취해주면 됨 (어떤 Client인지는 알 필요가 없다)
Client: 요청자. 내 요청을 누가(Receiver) 구체적으로 어떻게 처리할 지(ConcreteCommand)만 알고 있으면 됨
장점
작업을 수행하는 객체와 요청하는 객체를 분리하여 단일 책임 원칙에 부합
코드의 수정 없이 작업 수행 객체나 추가 구현이 가능하여 개방-폐쇄 원칙에 부합
커맨드 단위의 액션이 가능
단점
구조가 간단하지가 않아 이해가 쉽게 되지 않음
예시
리모컨으로 불을 킨다는 행동을 커맨드 패턴으로 표현하면 아래와 같습니다.
Invoker
public class ControllerInvoker { Command turnOn; Command turnOff; public void setCommand(Command turnOn, Command turnOff) { this.turnOn = turnOn; this.turnOff = turnOff; } public void turnOn() { this.turnOn.execute(); } public void turnOff() { this.turnOff.execute(); } }
Command
public interface Command { void execute(); }
ConcreteCommand
public class LightTurnOnCommand implements Command{ private final LightReceiver lightReceiver; public LightTurnOnCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.turnOn(); } } public class LightTurnOffCommand implements Command{ private final LightReceiver lightReceiver; public LightTurnOffCommand(LightReceiver lightReceiver) { this.lightReceiver = lightReceiver; } @Override public void execute() { lightReceiver.turnOff(); } }
Receiver
public class LightReceiver { public void turnOn() { System.out.println("turn on"); } public void turnOff() { System.out.println("turn off"); } }
Client
ControllerInvoker controllerInvoker = new ControllerInvoker(); LightReceiver lightReceiver = new LightReceiver(); LightTurnOnCommand lightTurnOnCommand = new LightTurnOnCommand(lightReceiver); LightTurnOffCommand lightTurnOffCommand = new LightTurnOffCommand(lightReceiver); controllerInvoker.setCommand(lightTurnOnCommand, lightTurnOffCommand); controllerInvoker.turnOn(); controllerInvoker.turnOff();
이와 같이 Invoker에 추가 명령을 등록하거나 수정할 수 있고 실행할 수 있습니다. 또한 명령이나 Receiver가 더 추가된다면 기존 코드 수정 없이 setCommand를 수정하면 됩니다.
파이썬
from abc import ABCMeta, abstractmethod class Command(metaclass=ABCMeta): @abstractmethod def execute(self): pass class TurnOnCommand(Command): def __init__(self, receiver): self.receiver = receiver def execute(self): self.receiver.turn_on() class TurnOffCommand(Command): def __init__(self, receiver): self.receiver = receiver def execute(self): self.receiver.turn_off() class LightReceiver: def turn_on(self): print('turn on') def turn_off(self): print('turn off') class ControllerInvoke: def __init__(self, turn_on, turn_off): self._turn_on = turn_on self._turn_off = turn_off def turn_on(self): self._turn_on.execute() def turn_off(self): self._turn_off.execute() light_receiver = LightReceiver() turn_on_command = TurnOnCommand(light_receiver) turn_off_command = TurnOffCommand(light_receiver) controller_invoke = ControllerInvoke(turn_on_command, turn_off_command) controller_invoke.turn_on() controller_invoke.turn_off()