3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【デザインパターン】JavaScriptで学ぶCommand

Last updated at Posted at 2021-09-04

#はじめに
現在デザインパターンについて勉強中です。
今回はCommandついてまとめました。

#Command
Commandはリクエストに必要な処理をオブジェクトとしてカプセル化し、必要に応じて実行するための、振る舞いに関するデザインパターンの一つです。
リクエストの処理と実行を分離することで、命令のキューイング、ロギング、アンドゥなどが可能になります。

例えば、プログラム内の全てのユーザの操作がCommandオブジェクトとして実装されれば、プログラムは最近実行されたコマンドのスタックを保持することができるようになります。
ユーザがコマンドをアンドゥする際、プログラムは最後に実行されたCommandオブジェクトを取得(pop)し、そのオブジェクトのundo()メソッドを実行します。

#実装例
Commandパターンで銀行でのお金の預貯金機能を実装します。
リクエストの処理(deposit, withdraw)をBankAccountクラス、実行(call, undo)をBankAccountCommandクラスに実装します。

BankAccountCommandcall()メソッドを実行する際に、コマンドが成功したかどうかをsucceedで管理します。
こうすることで、succeedがtrueの場合のみ、undo()メソッドを実行されるようにできます。

class BankAccount
{
  constructor(balance=0)
  {
    this.balance = balance;
  }

  deposit(amount)
  {
    this.balance += amount;
    console.log(
      `Deposited ${amount}, balance is now ${this.balance}`
    );
  }

  withdraw(amount)
  {
    if (this.balance - amount >= BankAccount.overdraftLimit)
    {
      this.balance -= amount;
      console.log(
        `Withdrew ${amount}, balance is now ${this.balance}`
      );
      return true;
    }
    return false;
  }

  toString()
  {
    return `Balance: ${this.balance}`;
  }
}
BankAccount.overdraftLimit = -500;

let Action = Object.freeze({
  'deposit': 1,
  'withdraw': 2
});

class BankAccountCommand
{
  constructor(account, action, amount)
  {
    this.account = account;
    this.action = action;
    this.amount = amount;
    this.succeeded = false;
  }

  call()
  {
    switch (this.action)
    {
      case Action.deposit:
        this.account.deposit(this.amount);
        this.succeeded = true;
        break;
      case Action.withdraw:
        this.succeeded = this.account.withdraw(this.amount);
        break;
    }
  }

  undo()
  {
    if (!this.succeeded) return;
    switch (this.action)
    {
      case Action.deposit:
        this.account.withdraw(this.amount);
        break;
      case Action.withdraw:
        this.account.deposit(this.amount);
        break;
    }
  }
}

以下のように、インスタンスを作ってcall()undo()を実行します。

let ba = new BankAccount(100);

let cmd = new BankAccountCommand(ba, Action.deposit, 50);
cmd.call();
console.log(ba.toString());

console.log('Performing undo:');
cmd.undo();
console.log(ba.toString());

すると、depositした分の残高balanceがアンドゥされたことを確認することができます。

Deposited 50, balance is now 150
Balance: 150
Performing undo:
Withdrew 50, balance is now 100
Balance: 100

#参考資料

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?