PerlでCommandパターン
Receiverクラスが実装。ReceiverクラスをCommandクラスでラップして実装をカプセル化。CommandクラスはAdapterパターン的に利用する。
ContextからRecieverを分離するため、Invokerを通じてCommandオブジェクトを呼び出す。下のプログラムにはないけど、InvokerへのCommandのセットをInvokerFactory的なクラスにまかせると、Contextの分離がよりハッキリする。
use strict; use warnings; use Data::Dumper; my $invoker = new Invoker(); my $fireReceiver = new Receiver("fire!"); my $fireCommand = new ConcreteCommand($fireReceiver); $invoker->setCommand($fireCommand); my $shockReceiver = new Receiver("shock!"); my $shockCommand = new ConcreteCommand($shockReceiver); $invoker->setCommand($shockCommand); my $godReceiver = new Receiver("oh my god!"); my $godCommand = new ConcreteCommand($godReceiver); $invoker->setCommand($godCommand); $invoker->invoke(0); $invoker->invoke(1); $invoker->invoke(2); #print Dumper($invoker); { package Receiver; sub new{ my $this = shift; my $msg = shift; return bless { msg => $msg, @_ }, $this; } sub action{ my $this = shift; my $msg = $this->{msg}; print "$msg @ Receiver\n"; } } { package Command; sub new{ my $this = shift; return bless { @_ }, $this; } sub invoke{ die("need overriding\n"); } sub undo{ die("need overriding\n"); } sub redo{ die("need overriding\n"); } } { package ConcreteCommand; sub new{ my $this = shift; my $receiver = shift; my $obj = new Command(receiver => $receiver, @_); return bless $obj, $this; } sub invoke{ my $this = shift; $this->{receiver}->action(); } sub undo{ die("need overriding\n"); } sub redo{ die("need overriding\n"); } } { package Invoker; sub new{ my $this = shift; my $commands = []; my $obj = { commands => $commands }; return bless $obj, $this; } sub setCommand{ my ($this, $command) = @_; push(@{$this->{commands}}, $command); } sub invoke{ my ($this, $index) = @_; ${$this->{commands}}[$index]->invoke(); } }
undo, redoを実装したい場合は
- Invoker, Commandにundo, redoインターフェースを追加
- Commandに編集時のデータを参照できるようにしておく