ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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()

    댓글