커맨드 패턴

2024. 10. 7. 10:57BackEnd(Java)/Design Pattern

✅ 아래 내용들에 대해서 알아보자

- 커맨드 패턴이란
- 코드를 보면서 커맨드 패턴을 이해해 보자
- 정리

 

커맨드 패턴

커맨드 패턴은 행동 요청을 객체로 캡슐화하여 요청을 나중에 사용할 수 있도록 저장하거나, 요청을 큐에 담거나, 로그로 남기거나, 되돌리기(undo) 등을 가능하게 하는 디자인 패턴입니다. 

 

이 패턴을 사용하면 요청을 클라이언트와 실행을 담당하는 객체 간에 분리할 수 있어 유연하고 확장 가능한 디자인이 가능합니다.

 

커맨드 패턴에는 인보커, 커맨드, 리시버, 클라이언트로 구성되는데 각각 역할에 대해서 알아봅시다.(아래 그림 참고)

 

커맨드 패턴의 전체적인 그림

 

 

1. 인보커(Invoker, 호출자)

인보커는 커맨드(Command) 객체를 저장하고 커맨드 객체의 execute를 호출하는 역할을 한다. 

인보커는 setCommand() 메서드를 제공하여 커맨드를 동적으로 변경할 수 있도록 제공한다.

/**
 * 인보커 객체
 */
public class SimpleRemoteControl {

    Command slot;

    public SimpleRemoteControl() {
    }

    public void setCommand(Command command) {
        slot = command;
    }


    /**
     * 커맨드 객체의 execute 실행
     */
    public void buttonPressed() {
        if (Objects.nonNull(slot)) {
            slot.execute();
        }
    }
}

 

 

2. 커맨드(Command, 명령)

커맨드는  execute 메서드등을 제공한다. execute 메서드는 특정 작업을 실행하는 로직을 담고 있다.

package com.design.designpattern.chap6.simple_remote_control;

/**
 * 커맨드 객체
 */
public interface Command {

    /**
     * 실행
     */
    void execute();

    /**
     * 실행취소
     */
    void undo();

    /**
     * 로그 기록용
     */
    default void store() {
        System.out.println("db에 로그 적재");
    }

    /**
     * 시스템 다운 시 해당 메서드로 마지막 작업부터 다시 시작
     */
    default void load() {
        System.out.println("마지막 작업부터 재시작");
    }
}

 

 

3. 커맨드 구성 객체

커맨드는 구성객체는 Command를 구현하는 클래스로 해당 클래스에는 Receiver 객체를 사용하여 로직 수행을 처리한다.

아래 코드에서는 Light Receiver 객체를 사용하여 특정 작업을 처리하도록 지시한다.

/**
 * 커맨드 구현 객체
 */
@AllArgsConstructor
public class LightOnCommand implements Command {

    //리시버 객체
    Light light;

    /**
     * 리시버 객체가 실제 로직 수행
     */
    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}

 

 

4. 리시버(Receiver, 수신자)

리시버는 실제로 특정 작업을 수행하는 클래스이다. 아래 코드에서는 불을 켜고 끄는 기능을 제공한다.

/**
 * Receiver(리시버, 수신자) 객체 
 *  -명령의 실제 작업을 수행하는 객체
 *  -Command 객체는 이 수신자에게 작업을 위임함
 */
@Slf4j
@AllArgsConstructor
public class Light {

    private String name;

    public void on() {
        log.info("Light on");
    }

    public void off() {
        log.info("Light off");
    }


}

 

 

5. 클라이언트

클라이언트는 Command, Receiver, Invoker를 설정하고 연결하는 역할을 수행한다.

    @Test
    @DisplayName("리모컨 컨트롤러 테스트")
    public void SimpleRemoteControlTest() {
        //given
        Logger mockLogger = mock(Logger.class);
        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
        Light light = new Light("test") {
            @Override
            public void on() {
                mockLogger.info("Light on");
            }
        };

        //인보커 생성 및 커맨드 설정
        SimpleRemoteControl simpleRemoteControl = new SimpleRemoteControl();
        simpleRemoteControl.setCommand(new LightOnCommand(light));

        //when
        simpleRemoteControl.buttonPressed();

        //then
        verify(mockLogger).info(captor.capture());
        Assertions.assertThat("Light on").isEqualTo(captor.getValue());
    }

 

 

커맨드 패턴 클래스 다이어그램

커맨드 패턴 다이어그램

 

 

정리

  • 커맨드 패턴을 사용하면 요청하는 객체와 요청을 수행하는 객체를 분리할 수 있다.
  • 분리하는 과정에서 커맨드 객체가 있으며 이 객체가 행동이 들어있는 리시버를 캡슐화한다.
  • 인보커는 무언가 요청할 때 커맨드 객체의 execute() 메서드를 호출하면 되므로 인보커도 실제 행동과 분리되어 있다.
  • 커맨드 패턴을 활용해서 로그 및 트랜잭션 시스템을 구현할 수 있다.

 


참고 자료

  • 헤드 퍼스트 디자인 패턴
반응형

'BackEnd(Java) > Design Pattern' 카테고리의 다른 글

어댑터 패턴  (0) 2024.10.08
싱글톤 패턴  (0) 2024.10.07
데코레이터 패턴  (0) 2024.09.24
옵저버 패턴  (0) 2024.09.23
전략 패턴  (2) 2022.10.10