🐡
Rubyで学ぶコマンドパターン (Command Pattern)
1. どんなもの?
コマンドパターンは、処理(操作)をオブジェクトとして抽象化し、リクエストをカプセル化するデザインパターンです。
これにより、操作の呼び出し元と実行元を分離し、操作を遅延実行したり、取り消し(Undo)を実現したりすることができます。
例えば、ユーザーインターフェースにおける「ボタン押下での処理」や、「キューイングされた操作の順次実行」などで活用されます。
2. 通常の実装方法と比べてどこがすごいの?
通常の方法
処理を直接クラス内に書くと、呼び出し元と実行元が密結合となり、柔軟性が失われます。
class Receiver
def execute_action
puts "Action executed"
end
end
# 呼び出し元
receiver = Receiver.new
receiver.execute_action
-
課題:
- 呼び出し元が直接処理内容を知る必要があるため、結合度が高くなる。
- 操作の履歴管理や取り消しが難しい。
コマンドパターンの利点
コマンドパターンを使うと、操作をオブジェクトとして分離できるため、柔軟性が向上します。
# コマンドの履歴を管理するクラス(操作の呼び出し元)
class CommandManager
def initialize(receiver)
@receiver = receiver
@history = []
end
def execute_action
@receiver.execute_action
@history << :execute_action
end
def undo_last_action
last_action = @history.pop
if last_action == :execute_action
puts "Undo action"
end
end
end
# 操作を実行するクラス(操作の実行元)
class Receiver
def execute_action
puts "Action executed"
end
end
# 実行例
receiver = Receiver.new # 操作の実行元
manager = CommandManager.new(receiver) # 操作の呼び出し元
# コマンドの実行
manager.execute_action # => "Action executed"
# コマンドの取り消し
manager.undo_last_action # => "Undo action"
-
利点:
- 操作を独立したオブジェクトとして扱える。
- 操作の履歴や取り消しを容易に実現可能。
3. 技術や手法の"キモ"はどこにある?
-
操作をオブジェクト化
- 処理をオブジェクトとして抽象化することで、操作を一元管理できます。
-
柔軟な拡張性
- 新しい操作を追加する際に、既存のコードに影響を与えることなく実現できます。
-
操作の履歴管理や取り消し
- 実行済みのコマンドを記録しておくことで、履歴管理やUndo機能が実現可能です。
4. 実装例
例: Undo機能の実装
アプリで特定の操作を取り消す(Undo)機能を実装します。
class Command
def execute
raise NotImplementedError, "This method should be overridden in subclasses"
end
def undo
raise NotImplementedError, "This method should be overridden in subclasses"
end
end
class CreateUserCommand < Command
def initialize(user_params)
@user_params = user_params
@user = nil
end
def execute
@user = User.create(@user_params)
puts "User created: #{@user.id}"
end
def undo
@user.destroy if @user
puts "User deleted: #{@user.id}"
end
end
# Invoker(呼び出し元)
class Invoker
def initialize
@commands = []
end
def execute_command(command)
@commands << command
command.execute
end
def undo_last_command
command = @commands.pop
command.undo if command
end
end
# 呼び出し元コード
invoker = Invoker.new
command = CreateUserCommand.new({ name: "Alice", email: "alice@example.com" })
invoker.execute_command(command) # => User created: 1
invoker.undo_last_command # => User deleted: 1
-
実装ポイント:
- 操作をオブジェクトとして管理し、実行と取り消しを柔軟に実現。
5. 議論はあるか?
メリット
- 操作を独立したオブジェクトとして扱えるため、再利用性や拡張性が向上。
- 操作の履歴管理や取り消しが容易に実現可能。
- 呼び出し元と実行元の依存を低減できる。
デメリット
- コマンドクラスの数が増えすぎると管理が煩雑になる。
- 操作が簡単な場合には、オーバーヘッドが大きくなる可能性がある。
議論
コマンドパターンは、柔軟性と拡張性を提供する強力なパターンですが、全ての操作に適用するとコードが冗長になるリスクがあります。適用範囲を慎重に見極めることが重要です。
6. まとめ
コマンドパターンは、操作をオブジェクト化することで、呼び出し元と実行元の分離を実現するデザインパターンです。
このパターンを活用することで、再利用性や拡張性を向上させることができますが、適用範囲を慎重に見極めることが重要です。
Discussion