小池啓仁 ヒロヒト応援ブログ By はてな

小池啓仁(コイケヒロヒト)の動画など。

小池啓仁 ヒロヒト応援ブログ By はてな

PerlでBuilderパターン! (デザインパターン)

Perlデザインパターン 第7弾(Builderパターン)

Builderパターンとは、Director(監督)の管理下、Builder(親方)が作成項目だけ指示して、ConcreteBuilder(大工) に Building(家) インスタンスを生成させるデザインパターンです。
また、BuilderとConcreteBuilderの関係は、Templateパターンになっていますね。

一般的なBuilderパターンのクラス図

                    関連                   集約
  +---------------+      +---------------+      +---------------+
  | Client        |----->| Director      |o---->| Builder       |
  +---------------+      +---------------+      +---------------+
  |               |      | builder       |      |               |
  +---------------+      +---------------+      +---------------+
  |               |      | construct     |      | buildPart1    |
  |               |      |               |      | buildPart2    |
  |               |      |               |      | buildPart3    |
  +-------+-------+      +---------------+      +---------------+
          |                                             #        
          |                                             | 継承   
          |                                             |        
          |                                     +-------+-------+
          +------------------------------------>|ConcreteBuilder|
                                                +---------------+
                                                |               |
                                                +---------------+
                                                |buildPart1     |
                                                |buildPart2     |
                                                |buildPart3     |
                                                |getResult      |
                                                +---------------+

サンプル

一番下で紹介している本の『Java言語で学ぶデザインパターン入門』に書かれているBuilderパターンのJavaでのサンプルをPerlで書き換えてみました。

サンプルのクラス図
  +---------------+     +---------------+     +--------------+
  | Main          |---->| Director      |o--->| Builder      |
  +---------------+     +---------------+     +--------------+
  |               |     | builder       |     |              |
  +---------------+     +---------------+     +--------------+
  |               |     | construct     |     | makeTitle    |
  |               |     |               |     | makeString   |
  |               |     |               |     | makeItems    |
  |               |     |               |     | close        |
  +-------+-------+     +---------------+     +--------------+
          |                                           #        
          |                                           | 継承       
          |                                +----------+----------+
          |                                |                     |
          |                        +-------+-------+     +-------+-------+
          |                        | TextBuilder   |     | HTMLBuilder   |
          |                        +---------------+     +---------------+
          |                        | buffer        |     | filename      |
          |                        +---------------+     | writer        |
          |                        | makeTitle     |     +---------------+
          |                        | makeString    |     | makeTitle     |
          |                        | makeItems     |     | makeString    |
          |                        | close         |     | makeItems     |
          |                        | getResult     |     | close         |
          |                        |               |     | getResult     |
          |                        +---------------+     +---------------+
          |                                ^                     ^
          |                                |                     |
          +--------------------------------+---------------------+
Builder.pm
package Builder;
use strict;
use warnings;

sub makeTitle { die "オーバーライド必須"; }
sub makeString { die "オーバーライド必須"; }
sub makeItems { die "オーバーライド必須"; }
sub close { die "オーバーライド必須"; }
1;
Director.pm
package Director;
use strict;
use warnings;

sub new {
    my $class = shift;
    my $self = {};
    $self->{builder} = shift;
    return bless $self, $class;
}
sub construct {
    my $self = shift;
    $self->{builder}->makeTitle('Greeting');
    $self->{builder}->makeString('朝から昼にかけて');
    $self->{builder}->makeItems([
        'おはようございます。',
        'こんにちは。',
    ]);
    $self->{builder}->makeString('夜に');
    $self->{builder}->makeItems([
        'こんばんは。',
        'おやすみなさい。',
        'さようなら。',
    ]);
    return $self->{builder}->close;
}
1;


TextBuilder.pm

package TextBuilder;
use strict;
use warnings;
use base 'Builder';

sub new {
    my $class = shift;
    my $self = {};
    $self->{buffer} = '';
    return bless $self, $class;
}
sub makeTitle {
    my $self = shift;
    my $title = shift;
    $self->{buffer} .= "===========================\n";
    $self->{buffer} .= "『$title』\n\n";
}
sub makeString {
    my $self = shift;
    my $str = shift;
    $self->{buffer} .= "■$str\n\n";
}
sub makeItems {
    my $self = shift;
    my $items = shift;
    for my $i (0..$#{$items}) {
        $self->{buffer} .= " ・$items->[$i]\n";
    }
    $self->{buffer} .= "\n";
}
sub close {
    my $self = shift;
    $self->{buffer} .= "==========================\n";
}
sub getResult {
    my $self = shift;
    return $self->{buffer};
}
1;
HTMLBuilder.pm
package HTMLBuilder;
use strict;
use warnings;
use FileHandle;
use base 'Builder';

sub new {
    my $class = shift;
    my $self = {};
    $self->{filename} = '';
    $self->{writer} = '';
    return bless $self, $class;
}
sub makeTitle {
    my $self = shift;
    my $title = shift;
    $self->{filename} = $title.".html";
    $self->{writer} = FileHandle->new("> $self->{filename}");
    $self->{writer}->print("<html><head><title>$title</title></head><body>\n<h1>$title</h1>\n");
}
sub makeString {
    my $self = shift;
    my $str = shift;
    $self->{writer}->print("<p>$str</p>\n");
}
sub makeItems {
    my $self = shift;
    my $items = shift;
    $self->{writer}->print("<ul>\n");
    for my $i (0..$#{$items}) {
        $self->{writer}->print("<li>$items->[$i]</li>\n");
    }
    $self->{writer}->print("</ul>\n");
}
sub close {
    my $self = shift;
    $self->{writer}->print("</body></html>\n");
    $self->{writer}->close;
}
sub getResult {
    my $self = shift;
    return $self->{filename};
}
1;
Main.pl
use strict;
use warnings;
use Director;
use TextBuilder;
use HTMLBuilder;

my ($args) = @ARGV;
$args = "" if (!defined($args)); 
if ($args eq 'plain') {
    my $TextBuilder = TextBuilder->new;
    my $director = Director->new($TextBuilder);
    $director->construct;
    my $result = $TextBuilder->getResult;
    print $result;
}
elsif ($args eq 'html') {
    my $HTMLBuilder = HTMLBuilder->new;
    my $director = Director->new($HTMLBuilder);
    $director->construct;
    my $filename = $HTMLBuilder->getResult;
    print "$filenameが作成されました。\n";
}
else {
    &usage;
}
sub usage {
    print "Usage: perl Main.pl plain    プレーンテキストで文書作成\n";
    print "Usage: perl Main.pl html     HTMLファイル文書作成\n";
}
実行結果
C:\Documents and Settings\dp\Builder>perl Main.pl plain
===========================
『Greeting』

■朝から昼にかけて

 ・おはようございます。
 ・こんにちは。

■夜に

 ・こんばんは。
 ・おやすみなさい。
 ・さようなら。

==========================
C:\Documents and Settings\dp\Builder>perl Main.pl html
Greeting.htmlが作成されました。

C:\Documents and Settings\dp\Builder>type Greeting.html
<html><head><title>Greeting</title></head><body>
<h1>Greeting</h1>
<p>朝から昼にかけて</p>
<ul>
<li>おはようございます。</li>
<li>こんにちは。</li>
</ul>
<p>夜に</p>
<ul>
<li>こんばんは。</li>
<li>おやすみなさい。</li>
<li>さようなら。</li>
</ul>
</body></html>

尚、本コンテンツは、結城先生の以下の本をかなり参考にしています。
Javaデザインパターンを勉強したい人には、お勧めのご著書です!

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門