Personal-Study/Design Patterns

[TS Design Patterns] 행동 패턴 - 커맨드

Aaron-Kim 2023. 11. 5. 00:09

Command (Action, Transaction)

- 요청을 요청에 대한 모든 정보가 포함된 독립 실행형 객체로 변환

- 다양한 요청들이 있는 메서드들을 인수화할 수 있고, 요청의 실행을 지연 또는 대기열에 넣을 수 있음,
   또 실행 취소할 수 있는 작업 지원

- 올바른 소프트웨어 디자인은 종종 관심사 분리의 원칙을 기반으로 함

- GUI 객체들이 비즈니스 로직에게 직접 요청을 보내지 않고
   모든 요청 세부 정보들을 요청을 작동시키는 단일 메서드를 가진 별도의 커맨드 클래스로 추출하라고 제안

- 커맨드 객체들은 다양한 GUI 객체와 비즈니스 논리 객체들 간의 링크 역할을 함

- GUI는 커맨드를 작동시킬 뿐 다른 어떤 것도 알 필요 없음

   커맨드가 모든 세부 사항들을 처리함

- 작업들로 객체를 매개변수화 하려는 경우에 사용

- 작업들의 실행을 예약하거나, 대기열에 넣거나, 원격으로 실행하려는 경우에 사용

- 되돌릴 수 있는 작업 구현하려고 할 때 사용

 

- 명령을 처리하는 객체를 통해 메서드와 실행되는 동작의 결합도를 낮출 수 있음

- 특정 작업을 실행하는 개체와 메서드를 호출하는 개체를 분리할 수 있음

- 객체와 메서드를 분리할 수 있게 해줌

   -> 수명이 지정된 명령을 만들거나, 명령들을 큐에 담아 특정한 시간대에 처리하는 것도 가능해짐

- 쓸만한 상황이 딱히 많지 않고 종종 불필요한 코드가 만들어짐

 

- 예시

 

/**
 * The Command interface declares a method for executing a command.
 */
interface Command {
  execute(): void;
}

/**
 * Some commands can implement simple operations on their own.
 */
class SimpleCommand implements Command {
  private payload: string;

  constructor(payload: string) {
    this.payload = payload;
  }

  public execute(): void {
    console.log(
      `SimpleCommand: See, I can do simple things like printing (${this.payload})`
    );
  }
}

/**
 * However, some commands can delegate more complex operations to other objects,
 * called "receivers."
 */
class ComplexCommand implements Command {
  private receiver: Receiver;

  /**
   * Context data, required for launching the receiver's methods.
   */
  private a: string;

  private b: string;

  /**
   * Complex commands can accept one or several receiver objects along with
   * any context data via the constructor.
   */
  constructor(receiver: Receiver, a: string, b: string) {
    this.receiver = receiver;
    this.a = a;
    this.b = b;
  }

  /**
   * Commands can delegate to any methods of a receiver.
   */
  public execute(): void {
    console.log(
      'ComplexCommand: Complex stuff should be done by a receiver object.'
    );
    this.receiver.doSomething(this.a);
    this.receiver.doSomethingElse(this.b);
  }
}

/**
 * The Receiver classes contain some important business logic. They know how to
 * perform all kinds of operations, associated with carrying out a request. In
 * fact, any class may serve as a Receiver.
 */
class Receiver {
  public doSomething(a: string): void {
    console.log(`Receiver: Working on (${a}.)`);
  }

  public doSomethingElse(b: string): void {
    console.log(`Receiver: Also working on (${b}.)`);
  }
}

/**
 * The Invoker is associated with one or several commands. It sends a request to
 * the command.
 */
class Invoker {
  private onStart: any;

  private onFinish: any;

  /**
   * Initialize commands.
   */
  public setOnStart(command: Command): void {
    this.onStart = command;
  }

  public setOnFinish(command: Command): void {
    this.onFinish = command;
  }

  /**
   * The Invoker does not depend on concrete command or receiver classes. The
   * Invoker passes a request to a receiver indirectly, by executing a
   * command.
   */
  public doSomethingImportant(): void {
    console.log('Invoker: Does anybody want something done before I begin?');
    if (this.isCommand(this.onStart)) {
      this.onStart.execute();
    }

    console.log('Invoker: ...doing something really important...');

    console.log('Invoker: Does anybody want something done after I finish?');
    if (this.isCommand(this.onFinish)) {
      this.onFinish.execute();
    }
  }

  private isCommand(object: any): object is Command {
    return object.execute !== undefined;
  }
}

/**
 * The client code can parameterize an invoker with any commands.
 */
const invoker = new Invoker();
invoker.setOnStart(new SimpleCommand('Say Hi!'));
const receiver = new Receiver();
invoker.setOnFinish(new ComplexCommand(receiver, 'Send email', 'Save report'));

invoker.doSomethingImportant();

 

class OrderManager {
  constructor() {
    this.orders = [];
  }

  execute(command, ...args) {
    return command.execute(this.orders, ...args);
  }
}

class Command {
  constructor(execute) {
    this.execute = execute;
  }
}

function PlaceOrderCommand(order, id) {
  return new Command(orders => {
    orders.push(id);
    console.log(`You have successfully ordered ${order} (${id})`);
  });
}

function CancelOrderCommand(id) {
  return new Command(orders => {
    orders = orders.filter(order => order.id !== id);
    console.log(`You have canceled your order ${id}`);
  });
}

function TrackOrderCommand(id) {
  return new Command(() =>
    console.log(`Your order ${id} will arrive in 20 minutes.`)
  );
}

const manager = new OrderManager();

manager.execute(new PlaceOrderCommand("Pad Thai", "1234"));
manager.execute(new TrackOrderCommand("1234"));
manager.execute(new CancelOrderCommand("1234"));

 

- 활용


[아티클]

[TS 사용 예시]

반응형