π§© Command Pattern
β Intentβ
- Encapsulate a request as a command object
- Useful for recording, re-executing, or scheduling operations
β Motivationβ
- The processing logic is passed in from the outside β
execute()
- Well-suited for Undo/Redo functionality or batch operations
β When to Useβ
- When operation history matters, such as in user actions or batch processing
- When operations need to be deferred or accumulated
β Code Exampleβ
- 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
β Explanationβ
This code applies the Command
pattern to encapsulate discount logic as command objects that can be dynamically executed.
The Command
pattern expresses operations as objects, enabling deferred execution, queuing, or undo capabilities.
1. Overview of the Command Patternβ
-
Command: Defines the interface for encapsulated operations
- Represented by
DiscountCommand
in this code
- Represented by
-
ConcreteCommand: Implements the
Command
interface and provides specific logic- Implemented by
StudentDiscountCommand
,MemberDiscountCommand
,VIPDiscountCommand
, andNoDiscountCommand
- Implemented by
-
Invoker: Executes the command
- Represented by
DiscountExecutor
- Represented by
-
Client: Creates the appropriate command and passes it to the invoker
- Represented by the part of the code that uses a
switch
statement to generate aDiscountCommand
and pass it toDiscountExecutor
- Represented by the part of the code that uses a
2. Key Classes and Their Rolesβ
-
DiscountCommand
- Common interface for discount operations
- Declares the method
execute(basePrice: number): number
-
StudentDiscountCommand
,MemberDiscountCommand
,VIPDiscountCommand
,NoDiscountCommand
- Concrete command classes implementing
DiscountCommand
- Each applies a different discount rate
- Concrete command classes implementing
-
DiscountExecutor
- The
Invoker
class - Uses
setCommand
to receive a command, andrun
to execute it
- The
-
Client
- Generates the appropriate command using a
switch
statement based on user type and passes it to theDiscountExecutor
- Generates the appropriate command using a
3. UML Class Diagramβ
4. Benefits of the Command Patternβ
- Encapsulation of Actions: Operations are expressed as objects, enabling deferred execution, queuing, and undo functionality
- Flexibility: New commands can be added simply by implementing
DiscountCommand
- Generic Invoker:
DiscountExecutor
can execute any command, increasing its reusability
This design improves flexibility and extensibility by treating operations as first-class objects. It is particularly useful when dynamic switching or history management is required.