読者です 読者をやめる 読者になる 読者になる

Decoratorパターン

perl

オブジェクトを包み込み、機能を装飾していくパターン。

メリット
  • 複数クラスを自由に組み合わせできる
  • 実行時に動的に機能を追加できる
  • つまり、継承よりも柔軟性がある
実装例
  • 元になるクラス: Text
package Text;

sub new {
    my $class = shift;
    my $text = shift;
    return bless(\$text, $class);
}

sub get_text {
    my $self = shift;
    return $$self;
}
1;
  • デコレーションクラス: TextDecorator::Exclamation
package TextDecorator::Exclamation;

my $decoration = "!!!";
our $AUTOLOAD;

sub new {
    my $class = shift;
    my $component = shift;
    bless({ component => $component }, $class);
}

sub get_text {
    my $self = shift;
    my $component = $self->{component};
    my $text = $component->get_text();
    return sprintf('%s%s%s', $decoration, $text, $decoration);
}

# ラッピングするオブジェクトにどんなメソッドがあっても対応できるように
sub AUTOLOAD {
    my $component = $$_[0];
    my $method = $AUTOLOAD;
    $method =~ s/.+:://;
    my @args = @_;
    $component->$method(@_);
}
1;
  • デコレーションクラス: TextDecorator::Question
package TextDecorator::Question;

my $decoration = "???";
our $AUTOLOAD;

sub new {
    my $class = shift;
    my $component = shift;
    bless({ component => $component }, $class);
}

sub get_text {
    my $self = shift;
    my $component = $self->{component};
    my $text = $component->get_text();
    return sprintf('%s%s%s', $decoration, $text, $decoration);
}

sub AUTOLOAD {
    my $component = $$_[0];
    my $method = $AUTOLOAD;
    $method =~ s/.+:://;
    my @args = @_;
    $component->$method(@_);
}
1;
  • 使う側
use Text;
use TextDecorator::Exclamation;
use TextDecorator::Question;

my $txt = new Text('Decorator Pattern');
print $txt->get_text(), "\n";

my $single = new TextDecorator::Exclamation($txt);
print $single->get_text(), "\n";

my $double = new TextDecorator::Question($single);
print $double->get_text(), "\n";

my $triple = new TextDecorator::Exclamation($double);
print $triple->get_text(), "\n";
  • 実行結果
kotaro@mac:~$ perl test.pl 
Decorator Pattern
!!!Decorator Pattern!!!
???!!!Decorator Pattern!!!???
!!!???!!!Decorator Pattern!!!???!!!
まとめ
  • Decoratorパターンを使うと、装飾をしていくように組み合わせで機能を追加できる
  • 実装例ではTextDecorator::Question, TextDecorator::Exclamationをそれぞれ用意したが、TextDecoratorクラスを用意して、これを継承するともっと便利かも