20 03 2006 designpattern Tweet
Factory Methodはちゃんと理解するのにちょっと時間がかかった。
- インスタンスの生成がTemplate Methodで抽象化されている
- つまりインスタンス化をサブクラスに任せる
というところがポイントだが、ここの部分は抽象メソッドでなくても、デフォルトのメソッドを用意してもいいし、エラー吐くようにしてもいいらしい。ココなど詳しいが、
naoyaのはてなダイアリー - prototype.js でデザインパターン - Factory Method
クライアントで利用する主人公であるところのインスタンスの生成ロジックを Factory の中に抽象化してやって、具体的な new によるインスタンス生成の束縛からクラスを解放してやりましょう、ということになります。
つまり、コンストラクトするメソッドを適当な方法で実際のクラスと切り離すという理解でいいのかな。
CPAN探してみたらClass::Factory というモジュールを見つけたので、これを使ってデザパタ本のサンプルを書いてみた。
まず、FactoryクラスはClass::Factoryを使うので特に書く必要はない(モジュールのインストールだけ)。で、IDCardFactoryクラスはこんな風に書くだけでラクチン。
package IDCardFactory; use strict; use base qw(Class::Factory); __PACKAGE__->register_factory_type( IDCard => 'IDCard' ); 1;
Productクラスもサンプルの通り、何はともあれuse出来るもの
package Product; use strict; sub use { die; } 1;
で、実際のプロダクトの生成メソッドはinitなのでここをちょっと書く(といってもサンプルのnewとほとんど変わらんが)
package IDCard; use strict; use base qw(Product); sub init { my ( $self, $owner) = @_; $self->{owner} = $owner; print "$self->{owner}のカードを作ります。\n"; return $self; } sub use { my $self = shift; print "$self->{owner}のカードを使います。\n"; } sub getowner { my $self = shift; return $self->{owner}; } 1;
さてメイン
#!/usr/bin/perl BEGIN { unshift(@INC, "./"); } use strict; use IDCardFactory; my $card1 = IDCardFactory->new('IDCard','kzfm'); my $card2 = IDCardFactory->new('IDCard','愛犬はな'); my $card3 = IDCardFactory->new('IDCard','U隊長'); my $card4 = IDCardFactory->new('IDCard','新入りゆき'); $card1->use; $card2->use; $card3->use; $card4->use;
で、実行結果
$./main.pl kzfmのカードを作ります。 愛犬はなのカードを作ります。 U隊長のカードを作ります。 新入りゆきのカードを作ります。 kzfmのカードを使います。 愛犬はなのカードを使います。 U隊長のカードを使います。 新入りゆきのカードを使います。
これだと、ファクトリーのインスタンスが作られないのがサンプルと違うとこかな。あとはClass::Factoryのメソッドregister_factory_typeで幾つかのプロダクトを登録できるので、IDCardFactoryではなくてCardFactoryにして、VISA,Master,Nojima,Kojima,Yamadaカードみたいに複数のカードを登録しとくのが正しい使い方なのかな。
このエントリは結城浩さんの本と、それに載っているサンプルをPerlで書いた例をみながら考えたことをメモってます。あとは、はてなの伊藤さんのjavascriptでの例も参考にしてます。 間違いの指摘とか、もっといいやり方教授してくれたらとても嬉しいゾ。