Design Pattern/행위 패턴

Command Pattern

커맨드 패턴(Command pattern)을 이용하면, 요구사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러가지 다른 요구사항을 집어넣을 수 도 있습니다. 또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업취소 기능도 지원이 가능합니다.

 

커맨드 객체는 일련의 행동을 특정 리시버하고 연결시킴으로써 요구 사항을 캡슐화한 것이라는 점을 이미 배웠습니다. 이렇게 하기 위해서 행동과 리시버를 한 객체에 집어넣고, execute()라는 메소드 하나만 외부에 공개하는 방법을 씁니다. 이 메소드 호출에 의해서 리시버에서 일련의 작업이 처리됩니다. 외부에서 볼 때는 어떤 객체가 리시버 역할을 하는지, 그 리시버에서 실제로 어떤 일을 하는지 알 수 없습니다. 그냥 execute() 메소드를 호출하면 요구 사항이 처리된다는 것만 알 수 있을 뿐이죠.

대표적인 예는 자바 thread가 있습니다. 구현 후 run 으로 실행시키는 부분을 생각해보시면 될거 같습니다.

 

이를 순서대로 구현하면서 보다 파악해볼려고합니다.

예시는 방의 물건을 리모컨으로 컨트롤 하는 상황을 예시로 합니다.

리모컨으로

방의 불을 키고 끄고

음악을 재생, 정지 하며 다음, 이전으로 컨트롤하고

커튼을 열고 닫는다.

 

1. Command 인터페이스

public interface Command {
    public void execute();
}

2. 리시버 객체 구현

각 리시버 객체들은 자신들이 하는 업무들이 있죠. 이를 구현합니다.

public class Curtain {

    public void open() {
        System.out.println("커튼 open");
    }

    public void close() {
        System.out.println("커튼 close");
    }
}
public class Light {
    public void on() {
        System.out.println("불 킴");
    }

    public void off() {
        System.out.println("불 끔");
    }
}
public class MusicPlayer {
    public void on() {
        System.out.println("음악 재생");
    }

    public void off() {
        System.out.println("음악 정지");
    }

    public void next() {
        System.out.println("다음 곡 재생");
    }

    public void pre() {
        System.out.println("이전 곡 재생");
    }
}

 

2. ConcretCommand 구현

상단의 설명에서 행동과 리시버를 한 객체에 집어 넣는다라고 하였는데 이 단계에서 진행됩니다.

public class CurtainCloseCommand implements Command{
    private Curtain curtain;

    public CurtainCloseCommand(Curtain curtain) {
        this.curtain = curtain;
    }

    @Override
    public void execute() {
        curtain.close();
    }
}
public class CurtainOpenCommand implements Command{
    private Curtain curtain;

    public CurtainOpenCommand(Curtain curtain) {
        this.curtain = curtain;
    }

    @Override
    public void execute() {
        curtain.open();
    }
}
public class LightOffCommand implements Command{
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }
}
public class LightOnCommand implements Command{
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }
}
public class MusicPlayerOnCommand implements Command{
    private MusicPlayer musicPlayer;

    public MusicPlayerOnCommand(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }
    @Override
    public void execute() {
        musicPlayer.on();
    }
}
public class MusicPlayerOffCommand implements Command{
    private MusicPlayer musicPlayer;

    public MusicPlayerOffCommand(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.off();
    }
}
public class MusicPlayerNextCommand implements Command{
    private MusicPlayer musicPlayer;

    public MusicPlayerNextCommand(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.next();
    }
}
public class MusicPlayerPreCommand implements Command{
    private MusicPlayer musicPlayer;

    public MusicPlayerPreCommand(MusicPlayer musicPlayer) {
        this.musicPlayer = musicPlayer;
    }

    @Override
    public void execute() {
        musicPlayer.pre();
    }
}
public class NoCommand implements Command{
    @Override
    public void execute() {
        System.out.println("리모컨 슬롯에 명령이 설정되어 있지 않습니다.");
    }
}

 

3.  인보커 구현

인보커라고 하는데 간단하게 리모컨 구현했다고 생각하시면 됩니다.

버튼을 누르면 각 command의 excute를 싱행 시키는 것이죠.

import java.util.Arrays;

public class RemoteController {
    static final int MAX_SLOT = 8;
    Command[] commands;

    public RemoteController() {
        commands = new Command[MAX_SLOT];
        Arrays.fill(commands, new NoCommand());
    }

    public void registerCommand(int slot, Command command) {
        commands[slot] = command;
    }

    public void buttonPress(int slot) {
        commands[slot].execute();
    }
}


4. Client 구현 & 결과

각 리시버 인스턴스를 만들어줍니다. 방에 3개의 장비가 존재하게 된다고 생각하시면 됩니다.

이후 각 버튼마다 Command를 등록해줍니다.

이후 버튼을 눌르면 끝입니다.

public class Client {
    public static void main(String[] args) {
        // 리시버 객체 생성
        MusicPlayer musicPlayer = new MusicPlayer();
        Curtain curtain = new Curtain();
        Light light = new Light();

        //인보커 객체인 리모컨 생성
        RemoteController remoteController = new RemoteController();

        // 인보커 객체인 리모컨은 커맨드 배열에 커맨드 저장
        remoteController.registerCommand(0, new MusicPlayerOnCommand(musicPlayer));
        remoteController.registerCommand(1, new MusicPlayerOffCommand(musicPlayer));
        remoteController.registerCommand(2, new MusicPlayerNextCommand(musicPlayer));
        remoteController.registerCommand(3, new MusicPlayerPreCommand(musicPlayer));

        remoteController.registerCommand(4, new CurtainOpenCommand(curtain));
        remoteController.registerCommand(5, new CurtainCloseCommand(curtain));

        remoteController.registerCommand(6, new LightOnCommand(light));
        remoteController.registerCommand(7, new LightOffCommand(light));

        // 인보커 객체인 리모컨 사용
        remoteController.buttonPress(0);
        remoteController.buttonPress(1);
        remoteController.buttonPress(2);
        remoteController.buttonPress(3);
        remoteController.buttonPress(4);
        remoteController.buttonPress(5);
        remoteController.buttonPress(6);
        remoteController.buttonPress(7);
    }
}

'Design Pattern > 행위 패턴' 카테고리의 다른 글

Template Method Pattern  (0) 2021.06.24
Strategy 전략 프로젝트에 적용 해보기  (0) 2021.05.28
State Pattern  (0) 2021.05.13
Strategy Pattern  (0) 2021.05.06