2005年7月7日木曜日

selectトリガを使ったClass::DBIのFactoryパターン

以前からClass::DBIでFactoryパターンを使えたらなーと思ってました。
たとえば、有料、無料、VIPのように複数の会員タイプがあるような会員サイトを作る場合、Class::DBIを継承したMyService::MemberがFactoryとなり、会員のタイプによって、MyService::Member::Basic, MyService::Member::Free, MyService::Member::VIPというようなサブクラスのインスタンスを作成するというようなイメージです。
さらにFactoryクラスでMyService::Member->retrieve_allとかすると、適切なサブクラスのインスタンスのリストがとれると最高です。

そんなことができないかなーとClass::DBIのドキュメントやソースを眺めていたんですが、Class::DBIに組み込まれているselectトリガを使えば実現できそうだったので、試してみました。
selectトリガはインスタンス生成時に必ず呼ばれているので、ここで会員のタイプを判別し、サブクラスにreblessしてやります。ソースコードはこんな感じです。このコードそのままではないですが、実際に同じようなコードを書いて試してみたところ、うまく動いているようです。

***MyService::Member - Factory
>

package MyService::Member
use base qw(Class::DBI);

require MyService::Member::Basic
require MyService::Member::Free
require MyService::Member::VIP

__PACKAGE__->set_db('Main', @datasource);
__PACKAGE__->table('member');
__PACKAGE__->columns(Primary => qw(member_id));
__PACKAGE__->columns(Essential => qw(member_type name age));

# select トリガにrebless用のメソッドをセット
__PACKAGE__->add_trigger( select => \&_rebless );

# サブクラスにrebless
sub _rebless {
my $self = shift;
#会員タイプ(member_type)でreblesするサブクラスを選択
my $class = 'MyService::Member::Basic';
if ($self->member_type eq 'Free'){
$class = 'MyService::Member::Free';
} elsif($self->member_type eq 'VIP'){
$class = 'MyService::Member::VIP';
}
bless $self, $class;
}

# abstract method
sub is_free {}
sub monthly_cost {}
<

*** MyService::Member::Basic - 一般会員
>

package MyService::Member::Basic;
use strict;
use base qw(MyService::Member);

sub is_free { 0 } # 無料ではない
sub monthly_cost { 500 } # 月額500円
<

*** MyService::Member::Free - 無料会員
>

package MyService::Member::Free;
use strict;
use base qw(MyService::Member);

sub is_free { 1 } # 無料
sub monthly_cost { 0 } # 月額0円
<

*** MyService::Member::VIP - VIP会員
>

package MyService::Member::VIP;
use strict;
use base qw(MyService::Member);

sub is_free { 0 } # 無料ではない
sub monthly_cost { 250 } # VIPは月額250円
<



0 件のコメント:

コメントを投稿