🧩 Command パターン
✅ 設計意図
- 処理そのものを「命令オブジェクト」として切り出す
- 処理を記録・再実行・スケジューリングするのが目的
✅ 適用理由
- 実行する処理を外から渡す →
execute()
- Undo/Redo やバッチ処理にも応用しやすい
✅ 向いているシーン
- ユーザー操作やバッチ処理のように、「処理の履歴」が重要
- 処理の実行を「遅延」や「蓄積」したいとき
✅ コード例
- TypeScript
- PHP
- Python
// コマンドインターフェース
interface DiscountCommand {
execute(basePrice: number): number;
}
// 各コマンド(ConcreteCommand)
class StudentDiscountCommand implements DiscountCommand {
execute(basePrice: number): number {
return basePrice * 0.8;
}
}
class MemberDiscountCommand implements DiscountCommand {
execute(basePrice: number): number {
return basePrice * 0.9;
}
}
class VIPDiscountCommand implements DiscountCommand {
execute(basePrice: number): number {
return basePrice * 0.7;
}
}
class NoDiscountCommand implements DiscountCommand {
execute(basePrice: number): number {
return basePrice;
}
}
// Invoker
class DiscountExecutor {
constructor(private command: DiscountCommand) {}
setCommand(command: DiscountCommand) {
this.command = command;
}
run(basePrice: number): number {
return this.command.execute(basePrice);
}
}
// 利用例
const userType = "member";
let command: DiscountCommand;
switch (userType) {
case "student":
command = new StudentDiscountCommand();
break;
case "member":
command = new MemberDiscountCommand();
break;
case "vip":
command = new VIPDiscountCommand();
break;
default:
command = new NoDiscountCommand();
}
const executor = new DiscountExecutor(command);
console.log(executor.run(1000)); // 900
<?php
interface DiscountCommand {
public function execute(float $basePrice): float;
}
class StudentDiscountCommand implements DiscountCommand {
public function execute(float $basePrice): float {
return $basePrice * 0.8;
}
}
class MemberDiscountCommand implements DiscountCommand {
public function execute(float $basePrice): float {
return $basePrice * 0.9;
}
}
class VIPDiscountCommand implements DiscountCommand {
public function execute(float $basePrice): float {
return $basePrice * 0.7;
}
}
class NoDiscountCommand implements DiscountCommand {
public function execute(float $basePrice): float {
return $basePrice;
}
}
class DiscountExecutor {
private DiscountCommand $command;
public function __construct(DiscountCommand $command) {
$this->command = $command;
}
public function setCommand(DiscountCommand $command): void {
$this->command = $command;
}
public function run(float $basePrice): float {
return $this->command->execute($basePrice);
}
}
// 利用例
$userType = "member";
switch ($userType) {
case "student":
$command = new StudentDiscountCommand();
break;
case "member":
$command = new MemberDiscountCommand();
break;
case "vip":
$command = new VIPDiscountCommand();
break;
default:
$command = new NoDiscountCommand();
}
$executor = new DiscountExecutor($command);
echo $executor->run(1000) . PHP_EOL; // 900
from abc import ABC, abstractmethod
class DiscountCommand(ABC):
@abstractmethod
def execute(self, base_price: float) -> float:
pass
class StudentDiscountCommand(DiscountCommand):
def execute(self, base_price: float) -> float:
return base_price * 0.8
class MemberDiscountCommand(DiscountCommand):
def execute(self, base_price: float) -> float:
return base_price * 0.9
class VIPDiscountCommand(DiscountCommand):
def execute(self, base_price: float) -> float:
return base_price * 0.7
class NoDiscountCommand(DiscountCommand):
def execute(self, base_price: float) -> float:
return base_price
class DiscountExecutor:
def __init__(self, command: DiscountCommand):
self.command = command
def set_command(self, command: DiscountCommand):
self.command = command
def run(self, base_price: float) -> float:
return self.command.execute(base_price)
# 利用例
user_type = "member"
if user_type == "student":
command = StudentDiscountCommand()
elif user_type == "member":
command = MemberDiscountCommand()
elif user_type == "vip":
command = VIPDiscountCommand()
else:
command = NoDiscountCommand()
executor = DiscountExecutor(command)
print(executor.run(1000)) # 900
✅ 解説
このコードは Command
パターン を使用して、割引計算のロジックをコマンドとしてカプセル化し、動的に切り替えられる設計を実現している。
Command
パターンは、操作をオブジェクトとして表現し、操作の実行を遅延させたり、キューに保存したり、取り消し可能にするデザインパターン。
1. Command パターンの概要
- Command: 操作を表現するインターフェース
- このコードでは
DiscountCommand
が該当
- このコードでは
- ConcreteCommand:
Command
を実装し、具体的な操作を定義するクラス- このコードでは
StudentDiscountCommand
,MemberDiscountCommand
,VIPDiscountCommand
,NoDiscountCommand
が該当
- このコードでは
- Invoker:
Command
を実行する役割を持つクラス- このコードでは
DiscountExecutor
が該当
- このコードでは
- Client:
Command
を生成し、Invoker
に渡す役割を持つ- このコードでは
switch
文でDiscountCommand
を生成し、DiscountExecutor
に渡している部分が該当
- このコードでは
2. 主なクラスとその役割
DiscountCommand
- 割引計算の共通インターフェース
execute(basePrice: number): number
メソッドを定義
StudentDiscountCommand
,MemberDiscountCommand
,VIPDiscountCommand
,NoDiscountCommand
DiscountCommand
を実装した具体的なコマンドクラス- 各クラスで異なる割引率を適用
DiscountExecutor
Invoker
クラスsetCommand
メソッドでコマンドを設定し、run
メソッドでコマンドを実行
Client
switch
文でユーザータイプに応じたコマンドを生成し、DiscountExecutor
に渡す
3. UML クラス図
4. Command パターンの利点
- 操作のカプセル化: 操作をオブジェクトとして表現することで、操作の実行を遅延させたり、キューに保存したりできる。
- 柔軟性: 新しい操作を追加する場合も、
DiscountCommand
を実装するだけで対応可能。 - Invoker の汎用性:
DiscountExecutor
はどのコマンドでも実行可能で、汎用性が高い。
この設計は、操作をオブジェクトとして扱うことで、柔軟性と拡張性を向上させる。特に、操作の切り替えや履歴管理が必要な場面で有効に機能する。