본문 바로가기

Develop/Design Pattern

[Design Pattern] 명령 (Command) 패턴

728x90

1. 개요

  • 명령 디자인 패턴 (Command Design Pattern)은 명령(Command)을 객체로 만들고 실행 작업을 명령한 객체(Invoke)와 실제 작업을 수행하는 객체(Receiver) 사이의 의존성을 제거하고, 명령의 매개 변수를 조작하거나 취소, 재실행하는 등의 기능을 하는 패턴이다.

2. 상황

  • 실행되는 작업을 객체로 캡슐화하고자 할 때
  • 작업을 취소하거나 다시 실행해야 할 때
  • 작업의 실행 순서를 관리해야 할 때
  • 다양한 작업을 동적으로 구성해야 할 때
  • 실행되는 작업과 호출하는 객체 간의 의존성을 줄여야 할 때

3. 다이어그램

command-default

3.1. 설명

  • Command
    • 명령 추상 클래스이다.
    • Receiver 필드를 가진다.
    • execute() 추상 메소드를 정의한다.
  • ConcreteCommand
    • Command 인터페이스의 구현 클래스이다.
    • execute() 메소드를 구현을 Receiver 에 위임한다.
  • Invoker
    • 명령을 실행하는 객체이다.
    • 요청을 수신하고, 해당 요청을 Command 객체에 전달하여 작업을 실행한다.
  • Receiver
    • 실제 작업을 수행하는 객체이다.
    • 명령에 의해 수행되어야 하는 작업이 구현되어 있다.

4. 구현

public abstract class Command { 
  protected Receiver receiver;

  public Command(Receiver receiver) {
    this.receiver = receiver;
  }

  public abstract void execute();
}

public class CommandA extends Command {
  public CommandA(Receiver receiver) { super(receiver); }

  @Override
  public void execute() { receiver.operationA(); }
}

public class CommandB extends Command {
  public CommandB(Receiver receiver) { super(receiver); }

  @Override
  public void execute() { receiver.operationB(); }
}
public class Invoker {
  private List<Command> commands = new ArrayList<>();

  public void add(Command c) { commands.add(c); }
  public void start() { 
    for (Command c : commands) c.execute();
  }
}
public class Receiver {
  public void operationA() {
    System.out.println("Receiver: Performing operationA");
  }
  public void operationB() {
    System.out.println("Receiver: Performing operationB");
  }
}
public class Client {
  public static void main(String[] args) {
    Invoker invoker = new Invoker();

    invoker.add(new CommandA(new Receiver()));
    invoker.add(new CommandB(new Receiver()));

    invoker.start();
  }
}

출력결과

Receiver: Performing operationA
Receiver: Performing operationB

5. 예시

public abstract class Command {
  protected Player player;

  public Command(Player player) { this.player = player; }
  public abstract void execute();
}

class MoveCommand extends Command {
  private int step;
  public MoveCommand(Player player, int step) {
    super(player); this.step = step;
  }

  @Override
  public void execute() { player.move(step); }
}

class TurnCommand extends Command {
  private Player.Direction dir;
  public TurnCommand(Player player, Player.Direction dir) { 
    super(player); this.dir = dir;
  }

  @Override
  public void execute() { player.turn(dir); }
}

class JumpCommand extends Command {
  public JumpCommand(Player player) { super(player); }

  @Override
  public void execute() { player.jump(); }
}
public class Invoker {
  List<Command> commands = new ArrayList<Command>();

  void add(Command c) { commands.add(c); }
  void start() {
    for (Command c : commands) c.execute();
  }
}
public class Player {
  enum Direction { LEFT, RIGHT }

  private String name;
  public Player(String name) { this.name = name; }

  void move(int step) { System.out.println(name + " moved " + step + " steps"); }
  void jump() { System.out.println(name + " jumped"); }
  void turn(Direction dir) { System.out.println(name + " turned " + dir.name()); }
}
public class Client {
  public static void main(String[] args) {
    Player player = new Player("Jane");
    Invoker invoker = new Invoker();

    invoker.add(new MoveCommand(player, 3));
    invoker.add(new TurnCommand(player, Player.Direction.LEFT));
    invoker.add(new JumpCommand(player));

    invoker.start();
  }
}

출력결과

Jane moved 3 steps
Jane turned LEFT
Jane jumped

6. 장단점

6.1. 장점

  • 코드의 유연성확장성을 제공한다.
  • 실행 취소(Undo)와 다시 실행(Redo) 기능 구현이 용이하다.

6.2. 단점

  • 코드의 복잡성이 증가한다.
  • 메모리 사용량이 증가한다.

관련 포스팅

728x90