たろマーク (はてなブックマーク)
-
[ ruby ][ rails ][ cms ] RESTful なのいいな
-
[ ar ] 楽しそう。宝物を置かせてもらったお店とかは宣伝になったりするのかな。
-
[ yokohama.pm ][ waf ]
-
[ ruby ]
■ perl で Observer パターン
Ruby で Observer パターンをやってみたところで、Perl でもやってみたくなったので書いてみました。お題はコードの世界に載っているテキスト表示の時計です。
ruby にある observer モジュールは include で mixin して使うので、Perl では Moose の Role を使って実装してみます。(モダン Perl 入門にもデザパタの章で Observer パターンがありますが、あえて ruby の observer モジュールを模してみます。)
で、これが Observable な Role です。これは時計の心臓部 Tick の Role なので Tick::Role::Observable としました。
package Tick::Role::Observable;
use Moose::Role;
has observer => (
is => 'rw',
default => sub { {} },
);
has state => (
is => 'rw',
default => 0,
);
sub is_changed {
my ($self) = @_;
return $self->state();
}
sub changed {
my ($self) = @_;
$self->state(1);
}
sub notify_observers {
my ($self, @args) = @_;
return unless $self->is_changed;
$self->state(0);
foreach my $package ( keys %{$self->observer} ) {
eval {
$self->observer->{$package}->update(@args);
};
if ( $@ ) {
confess $@;
}
}
return 1;
}
sub add_observer {
my ($self, $obj) = @_;
my $package = ref $obj;
confess $obj . 'is not object'
unless $package;
confess $package . 'can not update'
unless $obj->can('update');
$self->observer->{$package} = $obj;
}
1;
このモジュールを with 'Tick::Role::Observable'; するとそのモジュールに Observer な振る舞いが付与されます。
元のお題の時計モジュールはこんな感じ。
package Tick;
use Moose;
with 'Tick::Role::Observable';
__PACKAGE__->meta->make_immutable();
no Moose;
use DateTime;
use DateTime::TimeZone;
use Time::HiRes qw(sleep gettimeofday);
sub start {
my ($self, @args) = @_;
my $tz = DateTime::TimeZone->new( name => 'Asia/Tokyo' );
while (1) {
my $now = DateTime->now( time_zone => $tz );
$self->changed();
$self->notify_observers($now->hour, $now->minute, $now->second);
# micro second 単位のズレを調整
sleep 1.0 - (gettimeofday)[1] / 1000000.0;
}
}
1;
Tick->new->start すると1秒ごと sleep するループに陥ります。
1秒ごとに changed メソッドで更新フラグを立てて、notify_observers メソッドで監視者のオブジェクトに更新を通知します。
では、監視者として時刻をテキスト表示するオブジェクトを用意します。
package TextClock;
use Moose;
__PACKAGE__->meta->make_immutable();
no Moose;
sub update {
my ($self, @args) = @_;
local $| = 1;
printf "\e[8D%02d:%02d:%02d", @args;
}
1;
Observable なモジュールの監視者になるには update メソッドが必要です。
notify_observer メソッドで更新通知される際には update メソッドが呼ばれるからです。
なので、 TextClock モジュールにも update メソッドを用意します。
引数には notify_observer メソッドに渡された引数が全部渡ってきます。
さて、これで時計の心臓部である Tick モジュールと TextClock モジュールができました。この二つを監視者と被監視者としてスクリプトにまとめてみます。
use Tick; use TextClock; my $tick = Tick->new(); $tick->add_observer( TextClock->new() ); $tick->start();
add_observer に update メソッドを持ったオブジェクトを渡してあげると、監視者と被監視者の関係が簡単にできます。
このスクリプトを実行すると現在時刻がテキストで表示され、1秒ごとに更新されます。
肝は接点が add_observer メソッドだけなところですね。
時計の表示を変えたくなったら update メソッドを持ったオブジェクトを作って add_observer してあげるだけで良くなります。(とコードの世界に書いてあった)
自分が普段使ってる言語で書き直せたらそれなりに理解できてるかしら?
日経BP出版センター
売り上げランキング: 44091

Rubyに導入された思考法翔泳社
売り上げランキング: 18356

perl経験者は読んで損はしない
Perl中級者におすすめしたいトラックバック
このエントリーのトラックバックURL:
http://vkgtaro.jp/cgi-bin/mt/mt-tb.cgi/668






