第134回 モンキーパッチングをPerlでやるとこうだろうか
まつもとゆきひろ コードの世界?スーパー・プログラマになる14の思考法
- 作者: まつもとゆきひろ,日経Linux
- 出版社/メーカー: 日経BP社
- 発売日: 2009/05/21
- メディア: 単行本
- 購入: 50人 クリック: 1,711回
- この商品を含むブログ (110件) を見る
簡単に言うと、クラスを後から(実行時に)書き換えて、能力を追加することである。
起源は他人のバグを後付けで回避するところから始まったとのことです。
Ruby
class Bar def initialize(n) @name = n end def say1 puts @name + 'です' end end bar = Bar.new('バー'); bar.say1 class Bar def say2 puts @name + 'やがな' end end bar.say2
結果:
バーです バーやがな
Perl
Perlにはクラスというのは存在せず、ただパッケージに属した関数を呼んでいるだけなので、
この書き方はできません。
use strict; package Bar; sub new { my $thing = shift; my $class = ref $thing || $thing; bless { @_ }, $class; } sub say1 { my $self = shift; print $self->{'name'} . 'です' ."\n"; } package main; my $bar = Bar->new( 'name' => 'バー' ); $bar->say1(); sub say2 { my $o = shift; print $o->{'name'} . 'やがな' . "\n"; } say2($bar);
結果:
バーです バーやがな
このようにsay2関数を作って、オブジェクト指向の書き方を崩すしか方法はないのでしょうか?
第1引数は暗黙的に補われる書式を思い出してみる
$o->say();
という書き方をした場合、$oがsayメソッドの第1引数に渡る事を思い出してみると、
use strict; package Bar; sub new { my $thing = shift; my $class = ref $thing || $thing; bless { @_ }, $class; } sub say1 { my $self = shift; print $self->{'name'} . 'です' ."\n"; } package main; my $bar = Bar->new( 'name' => 'バー' ); $bar->say1(); sub say2 { my $o = shift; print $o->{'name'} . 'やがな' . "\n"; } $bar->main::say2(); # ここをオブジェクト指向っぽく書けた
結果:
バーです バーやがな
このようにオブジェクト指向の書き方で書けました。
結局のところ、やはりインスタンス変数にあたるモノが属しているパッケージの関数を呼ぶわけではなく、強引にmainパッケージの関数を呼び出す書き方をしているだけですので、ここまでする必要があるかはいささか怪しいところがありますが、このようにも書けますということでした。
そもそもRubyはクラスを編集している
そもそもRubyはクラスを編集できているので、あたらしくそのクラスを元にnewしたものは追加されたメソッドが呼べます。
class Bar def initialize(n) @name = n end def say1 puts @name + 'です' end end bar = Bar.new('バー') bar.say1 class Bar def say2 puts @name + 'やがな' end end bar.say2 other_bar = Bar.new('他のバー') other_bar.say1() other_bar.say2()
結果:
バーです バーやがな 他のバーです 他のバーやがな
オープンクラスすごしです。
Perlはあくまでもmainのsay2を呼んだだけですので、
$other_bar = Bar->new( 'name' => '他のバー' );
としたところで、
$other_bar->main::say2();
としなければなりません....
※もっと良い方法がある。その方法だと、こんな不具合がでる。ということをご存知の方がいらっしゃいましたら教えていただけるとうれしいです。