Perl̲オブジェクト 04.1.21 20:22 ページ92)
3▼第 3 章

デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介㈱クレイフィッシュ 小山 浩之 OYAMA Hiroyuki oyama@crayfish.co.jp

ではどのようにすれば巧い設計ができるようになる

はじめにのでしょうか．
1 つは経験を積むことです．もう1 つは先人の設計，

昨今オブジェクト指向は，さまざまな言語のさまざ

特に有用性が認められ幅広く利用されている設計を真

まな分野で活発に研究されています．オブジェクト指似ることです．経験は時間をかけて積むとして，どの向の概念はプログラミング言語を横断して共有できる

ような手段でその巧い設計を探し出せば良いのでしょため，研究結果を共通の資産として活用することがでうか．その解はデザインパターンを紐解くことです．

きます．つまりオブジェクト指向機能を備えたPerl にもその研究結果，知識，ノウハウを流用することがで

デザインパターンとはきるのです．
ところが，CGI 用スクリプトとして流通するコード

デザインパターンはオブジェクト指向のソフトウェ

で参考にできるものが少ない上，手続き指向言語とアを設計する際の定石で，この定石を集めたパターン

してのノウハウの蓄積が多いためか，Perl のオブジェ

カタログは書籍やオンラインで参照することができま

クト指向機能はあまり積極的に利用されていないのがす．デザインパターンはオブジェクト指向のソフトウ

現実です．オブジェクト指向がもたらすデータのカプェアで繰り返し利用される重要な設計に，体系的に名

セル化，ポリモーフィズム，継承などによるメリット

前をつけ，説明・評価し，その設計経験を第三者が効を無視するのはあまりにもったいないことです．

果的に利用できるように整理したもの，つまりお約束

注1

今回はPerlのオブジェクト指向機能の解説からはじまり，Webアプリケーション開発中に発生する問題に対してデザインパターンを適用して解決してゆく方法を述べていきます．

オブジェクト指向の難しさ

集・ネタ帳と言えます．

パターンカタログ
デザインパターンのパターンカタログには¡名前：そのパターンの名称¡目的：そのパターンの主な目的¡問題：どのような問題に当てはめられるか

オブジェクト指向の構文自体は単純で，誰でもすぐにコードを記述することができます．しかしオブジェ
クト指向による設計，それも変更に強く再利用可能な設計は正直難しいことです．
注 1）Perl 普及の原動力の 1 つであったとも言えますが．

92● WEB +DB PRESS Vol.6¡解法：そのようにその問題を解決するか¡結果：その設計によってどのようなメリットとデメリットが生まれるかPerl̲オブジェクト 04.1.21 20:22 ページ93

特集 2が似合う人になろう

がまとめられています．ある程度複雑さをもったもの

持っています．要は変数とそれを操作するための関数を言い表すときに名前が決まっていると，意思疎通やを集めたものとも言えます．オブジェクトを利用する記憶がスムーズにいきます．自分以外の人にどんな設ためには以下のような手続きを踏みます．

計を採用しているか，どんな設計をすれば良いかを伝えるとき「ここは〜を使っているよ」「そこは〜にす
れば良くない？」と一言で言えたら便利ですよね．

パターンの種類

qクラスを定義する
wクラスをインタンス化する
eインスタンスを操作する
rインスタンスを破棄する

デザインパターンはErich Gamma，Richard Helm，

それでは日付のデータを日付の文字列に変換する

Ralph Johnson，John Vlissidesら4人による通称GoF

DateStringクラスを例にして，オブジェクトの利用手（Gang Of Four）パターンが有名です．

順と用語を確認していきます．

パターンはこのほかにもさまざまな種類がまとめら

リスト1に挙げたDateStringクラスはDateString.pm

れています．ビジネスモデリングのパターンをまとめというファイル名で保存します．DateStringクラスをたアナリシスパターンなどマクロ的な視点のパターン利用するスクリプト（リスト2）を実行するともあれば，GoF パターンなどのようにミクロ的な視点のパターンもあります．
◇ ◇ ◇Sun Oct 21 16:59:24 2001

といった現在の日付を表す文字列が出力されます．

今回はさまざまなパターンの中からオブジェクト指
向プログラミングのスキルアップや効率化に有効と思われる，GoF パターンを主に扱います．

Perl のオブジェクト
指向のおさらい■クラスを定義するクラスの定義はpackage オペレータを使用します．
package を宣言してからコードの終端に達するか別のpackage を宣言する，もしくはスコープを外れるまでが1 つのクラスを表します．この例ではデザインパターンの話に移る前に，まずPerlのオブジェクト指向機能のおさらいをします．

オブジェクトオブジェクトは，あるデータ構造とその処理方法を

package DateString;

でDateStringクラスの定義を始めることを宣言しています．
クラスを利用するためにはクラスのインスタンスを得る必要があり，そのインスタンスを生成するための●リスト 1 DateString クラス
package DateString;use strict;
sub new{
my $class = shift;
my $time = shift || time();return bless { time => $time }, $class;}

メソッドをコンストラクタと呼びます．
コンストラクタはクラスに結び付けたデータのリファレンスを返すメソッドで，DateString クラスではサブルーチンnew( )がコンストラクタです．コンストラ
クタnew は，引数で受け取ったdelta 秒を格納するハ●リスト 2 DateString クラスの使用
sub to_string{
my $self = shift;
return scalar localtime $self->{time};}
1;__END__#!/usr/bin/perluse DateString;
my $date = DateString->new;print $date->to_string;__END__WEB +DB PRESS Vol.6 ● 93Perl̲オブジェクト 04.1.21 20:22 ページ94

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介

ッシュを作成しそのリファレンスをクラスに結び付け定します．bless されるデータはハッシュでも配列でて返します．引数が指定されない場合はデフォルトのもスカラでも型グロブでも，リファレンスであれば何動作として現在時刻を格納します．

でも構いません．
Java やC++ などデフォルトコンストラクタが用意さ

これを少し冗長に記述するとれる言語とは異なり，Perlは明示的にコンストラクタ
sub newを定義する必要があります．これはオブジェクトが内{包するデータに任意のデータ型を使用でき，これを指my $class = shift;

定するためです．

my $time = shift || time();◇ ◇ ◇my $date = { time => $time };return bless($data, $class);}

クラスをインスタンス化しただけでは何も仕事ができませんので，コンストラクタに続いて日付のデータ
をctime形式の文字列に変換するto̲stringメソッドをといった記述になります．

定義します．

また，Perl のコンストラクタには任意の名前をつけることができます．DateStringクラスの例ではnewという一般的な名称を使用していますが，instanceなど

sub to_string{
my $self = shift;

別の名前でもかまいません．

return scalar localtime $self->{time};

コンストラクタの第1 引数には自動的にクラス名が渡されます．よってnewメソッドの変数$classはクラス名の文字列 DateString を受け取っています．コンストラクタに引数を指定する場合は}メソッドの第1 引数は自動的にそのオブジェクトのインスタンスが渡されます（図 2）．この例では変数$selfにインスタンスを受け取っています．このインス

my $tomorrow = time() + 60 * 60 * 24;
my $date = DateString->new($tomorrow);

タンスを通してコンストラクタで定義したデータ構造にアクセスできます．DateStringクラスで使用するデと指定します．コンストラクタの内部では第1 引数にータはハッシュなので，ハッシュのリファレンスとしクラス名が渡され，指定した引数は第2 引数以降に渡てアクセスします．

されます（図1）．
データとクラスの結び付けは，bless オペレータが行います．bless は第1 引数にデータのリファレンス，$self->{time};

仮にオブジェクトにデータをセットする場合は第2 引数にそのデータを結び付けるクラスの名前を指my $yesterday = time() - 60 * 60 * 24;$self->{time} = $yesterday;

図 1 ／コンストラクタの呼び出し
DateString->new(time()＋06*06*24);

図 2 ／メソッドの呼び出し$date->to̲string();

package DateString;
sub newa
my $class = shift;
my $time = shift‖time();return bless a time => $tme s, $class;s

94● WEB +DB PRESS Vol.6

sub to̲stringamy $self = shift;
return scalar localtime $self->atimes;sPerl̲オブジェクト 04.1.21 20:22 ページ95

特集 2といった記述でデータをセットできます．

が似合う人になろう
れるので，プログラマが明示的に行う必要はありません．Perlは誰からも参照されなくなった変数（インス■クラスをインスタンス化するタンス）をガーベッジコレクションの対象として破棄

これでクラスの定義が終わりました．続いてこのク

します．つまりレキシカル変数はスコープを外れるな

ラスを使用してみます．クラスをインスタンス化する

ど，他のオブジェクトから参照されなくなった時点でためにはそのクラスのコンストラクタを呼び出します．

ガーベッジコレクションの対象になります．
また，インスタンスが消滅するタイミングで呼び出

my $date = DateString->new;

される特別なメソッド（デストラクタ）を定義するこ

この記述で先程定義したコンストラクタnewが実行ともできます．DESTROYという名前で定義したメソ

され，DateString クラスのインスタンス（オブジェク

ッドがデストラクタになり，インスタンスが消滅する

ト）を返します．インスタンスは任意の変数型で保持直前に呼び出されます．

することができ，この例ではスカラ変数$date にイン継承

スタンスを受け取っています．

継承とは，既存のクラスを元にして，機能を追加し■インスタンスを操作する
生成したインスタンスを操作するためにメソッドをた新しいクラスを少ない手数で定義できる方法です．
また，継承は重複したコードを抜き出して1 つにまとめる手段としても利用されます．

呼び出します．

継承したクラスは継承する元のクラス（スーパクラ
print $date->to_string;ス）が実装しているメソッドを引き継ぎます．その引これによりDateString クラスで定義したメソッド

き継いだメソッドをオーバライド（上書き）して動作

to̲string が実行され，変換された日付の文字列が返をカスタマイズしたり新しくメソッドを追加したりとされるので，標準出力にいった，差分だけをコーディングすることが可能にな
ります．これにより既存のコードの編集や新たにコピ

Sun Oct 21 16:59:24 2001

ー&ペーストするような，リスクを伴う変更を行わずに済みます．

と出力します．

◇ ◇ ◇■インスタンスを破棄する
使用されなくなったインスタンスは自動的に破棄さでは先ほどのDateString クラスを継承して，HTTP
で利用されるRFC1123 形式の日付の文字列を返す●リスト 3 DateStringRfc1123 クラス
package DateStringRfc1123;
use base 'DateString';
use constant RFC1123_FORMAT => '%s, %02d %s %04d %02d:%02d:%02d GMT';
sub to_string{
my $self = shift;
my @gmt = gmtime $self->{time};
return sprintf RFC1123_FORMAT,(qw(Sun Mon Tue Wed Thu Fri Sat))[$gmt[6]],$gmt[3],(qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$gmt[4]],$gmt[5] + 1900,$gmt[2], $gmt[1], $gmt[0];}1;__END__WEB +DB PRESS Vol.6 ● 95

Perl̲オブジェクト 04.1.21 20:22 ページ96

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介

DateStringRfc1123 クラスを作成します．リスト3 を

use base qw(DateString JapaneseString);

見てください．
Perlでクラスを継承する場合はbaseプラグマを使用します．DateStringRfc1123 クラスはDateString クラスを継承するために以下のように宣言します．

配列@ISA で指定する場合も同様です．
DateString クラスを継承したDateStringRfc1123 ク
ラスでは，to̲stringメソッドだけが実装されていますが，DateStringから受け継いだコンストラクタnewを

package DateStringRfc1123;
use base 'DateString';

これによりDateStringRfc1123 はDateString クラス

同様に使用することができます．
リスト4 のコードを実行するとRFC1123の形式で現在の日付の文字列を得ることができます．

を継承したクラスとして宣言されます．version 5.004などの若干古いバージョンのperl を使用する場合は，base プラグマを使わずに配列@ISA に継承するクラス
名をセットして指定します．

Sun, 21 Oct 2001 16:59:24 GMT

DateStringクラスで定義したto̲stringメソッドではなく，DateStringRfc1123 クラスで定義したto̲string
メソッドが呼び出されていることがわかります．このpackage DateStringRfc1123;@ISA = qw(DateString);

この方法は最新のversion 5.6.1 などでも使用することができますが，見通しの良さからbaseプラグマを使用する方法をお勧めします．
仮に複数のクラスから継承（多重継承）する場合
は，baseプラグマに配列でスーパクラスの名前を指定ようにスーパクラスで定義されているメソッドを上書きしてカスタマイズすることをオーバライドと呼びます．
同様にDateStringクラスを継承してRFC850の形式の日付文字列を出力するDateStringRfc850クラスを定義しましょう（リスト 5）．このクラスで定義した
to̲string メソッドはします．
Sunday, 21-Oct-01 16:59:24 GMT●リスト 4 DateStringRfc1123 クラスの使用#!/usr/bin/perluse DateStringRfc1123;
my $date = DateStringRfc1123->new;print $date->to_string;__END__という形式で日付の文字列を出力します．

動的束縛とポリモフィズム
オブジェクト指向プログラミングで得られる最も大きなメリットの1 つとして，動的束縛とポリモフィズ●リスト 5 DateStringRfc850 クラス
package DateStringRfc850;
use base 'DateString';
use constant RFC850_FORMAT => '%s, %02d-%s-%02d %02d:%02d:%02d GMT';
sub to_string{
my $self = shift;
my @gmt = gmtime $self->{time};
return sprintf RFC850_FORMAT,(qw(Sunday Monday tuesday Wednesday Thursday Friday Saturday))[$gmt[6]],$gmt[3],(qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$gmt[4]],$gmt[5],$gmt[2], $gmt[1], $gmt[0];}1;__END__

96● WEB +DB PRESS Vol.6

Perl̲オブジェクト 04.1.21 20:22 ページ97

特集 2が似合う人になろう

ムを利用できる点が挙げられます．例としてリスト6

ことができるオブジェクト同士を，置き換えて処理でを見てください．

きるような特性をポリモフィズムと呼びます．

このコードはDateString，DateStringRfc1123，Date

さらにPerlで動的束縛が行われる場面はインスタン

StringRfc850 それぞれのインスタンスを配列@date にスとメソッドの関係に留まらず，クラスとコンストラ

格納した後，配列@dateからインスタンスを取り出しクタやメソッド自身に対しても行われます．リスト7て，無造作にto̲string メソッドを呼び出します．こは先ほどあげたコードと同じ結果を出力します．つまれを実行すると次のような出力が得られます．

りPerl はクラス名を元にしてそのクラスを直接インスタンス化できるのです．たとえば以下のような記述でSun Oct 21 16:59:24 2001

もインスタンスを生成できます．

Sun, 21 Oct 2001 16:59:24 GMT"DateString"->new;Sunday, 21-Oct-01 16:59:24 GMT

もしこのような処理をオブジェクト指向ではない別の記述の仕方をしたならばどうなるでしょう．おそら

また，メソッド名を文字列で指定することで呼び出すメソッドを動的に指定することができます．

く日付の形式ごとに別々の名前で関数を用意し，どの形式で出力すべきか調べてから適切な関数を個別に

my $method = 'to_string';$date->$method();

呼び出すようなコードになるでしょう．
それをこのコードではインスタンス化した後は，ただ

静的なプログラミング言語を使っていて感じる「こんな風に書けたら良いのになぁ」をPerlでは簡素に直接記述することができます．

$date->to_string;

その他の情報源とメソッドを呼び出すだけで済んでいます．条件フラグも，突然表れる謎の識別番号も必要ありません．サブルーチンと処理が固定された関係にあるのとは異な駆け足で説明を進めてしまいましたが，Perlのオブり，この例のようにメソッドに対応した処理が実行時

ジェクト指向機能は本稿末の参考文献で挙げた書籍なに決定されることを動的束縛と呼びます．

どで詳細に解説されています．そこでもし不明な点がまた例の中のスカラ変数$data はDateString クラス

あったならば，Perl のオブジェクト指向機能についてのインスタンスであるときもあれば，DateStringRfc1123のメーリングリスト Object-oriented with Perl

クラスやDateStrinRfc850クラスのインスタンスである

覗いて，主旨をご理解いただいた上で参加してみてくときもあります．このように同じメソッドを受け取るださい．何かお役にたてるかもしれませんので．

を●リスト 6 ポリモフィズムの例#!/usr/bin/perluse DateString;use DateStringRfc1123;
use DateStringRfc850;
my @date;push @date, DateString->new;push @date, DateStringRfc1123->new;push @date, DateStringRfc850->new;foreach my $date (@date) {print $date->to_string, "\n";}__END__●リスト 7 クラスを直接インスタンス化する例#!/usr/bin/perluse DateString;use DateStringRfc1123;
use DateStringRfc850;
for my $class (qw(DateString DateStringRfc1123 DateStringRfc850)){
my $date = $class->new;print $date->to_string, "\n";}__END__WEB +DB PRESS Vol.6 ● 97

Perl̲オブジェクト 04.1.21 20:22 ページ98

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介生まれるため，修正に要するコストが高くなってしま

デザインパターンの実例います．
ヒントを得るためにデザインパターンのパターンカ

Webアプリケーションで行われる処理は比較的定型

タログを見ると，目的に「1 つのオペレーションにア的な処理が多く，発生するコーディング上の問題や解

ルゴリズムのスケルトンを定義しておき，その中のい決すべき技術的なポイントも似通っています．ここでくつかのステップについては，サブクラスの定義に任は一般的なWebアプリケーションの開発中に表れるコせることにする」を掲げるTemplate Methodパターン

ーディング上の問題を題材とし，デザインパターンをが参考になりそうです．

参考にしてそれらを解決していってみましょう．

Template Method パターン

ここで言うアルゴリズムは¡要求の解析
¡必要な処理掲示板の参照と書き込みを行う場合を考えてみます．Webアプリケーションのほとんどは一定処理手順¡コンテンツの生成¡コンテンツの出力を踏んで実行されるのですが，要求の種類を判定するための条件分岐が処理フェーズごとに表れてしまうコなどWebアプリケーションで行われる一連の流れを指ードをよく見かけます（リスト8）．

します．まずこの流れを定義するApplicationHandler

このようにコードに条件分岐がちりばめられていると理解の妨げになるばかりか，コードに変更を加える
たびに各々の条件分岐を意識しながら修正する必要が●リスト 8 Template Method 適用前#!/usr/bin/perl
use strict;my @message = qw(
MESSAGE1MESSAGE2MESSAGE3);
my $mode;if ($ENV{QUERY_STRINGS} =˜ m{^mode=write}) {$mode = 'write';}else {$mode = 'show';}●リスト 9 ApplicationHandler クラス
package ApplicationHandler;use strict;
sub new{
my $class = shift;
my $query = shift || $ENV{QUERY_STRINGS};
bless {
content => '',query=> $query,message => [qw(MessageA MessageB MessageC)],}, $class;}sub compute{
my $self = shift;$self->do_task;$self->do_create_content;$self->do_return_content;}
sub do_task {}if ($mode eq 'write') {if ($ENV{QUERY_STRINGS} =˜ m{^mode=write?message=(.*)$}) {push @message, $1;}}
my $content;
if ($mode eq 'write') {$content = join "\n", 'In Write Mode', @message;} else {$content = join "\n", 'In Show Mode', @message;}
print 'Content-Type: text/html', "\n\n";print $content;__END__

98● WEB +DB PRESS Vol.6

sub do_create_content{
my $self = shift;
my $messages = $self->{message};$self->{content} = join "\n", @$messages;}
sub do_return_content{
my $self = shift;print "Content-Type: text/plain\n";print $self->{content};}
1;__END__

Perl̲オブジェクト 04.1.21 20:22 ページ99

特集 2

クラス（リスト9）を作成します．

が似合う人になろう
Handler クラスを継承します．ApplicationHandler ク

ApplicationHandler クラスではフェーズごとに割りラスで定義したcomputeメソッドはこれらのクラスに当てられたメソッドdo̲task，do̲create̲content，も受け継がれるため，リスト13 のように使用します．

do̲return̲content が定義されています．これらのメ

このようにそれぞれの処理フェーズに散らばっていソッドはcompute メソッドから逐次呼び出されます

た条件分岐は，生成するインスタンスを決定する部分（図3）．

だけに集約され，見通しの良いコードになりました．

ApplicationHandler クラスを使用するコード，リス
ト10 を実行するとTemplate Methodパターンはオブジェクト指向プログ
ラミングの中でも基礎の基礎で，あらゆるところでその実装を見ることができます．

Content-Type: text/plain
Hello Object World

Factory Method パターンという出力が得られます．

先ほど挙げたコードの冒頭に，書き込みモードか読

続いてメッセージを参照する場合の処理を定義する

み込みモードかを判断して必要なクラスのインスタン

ShowHandler クラス（リスト11）を作成します．まスを生成する処理があります．要求を解釈して必要なた同様にメッセージを書き込む場合の処理を定義する

クラスのインスタンスを生成して返すという単純な処

WriteHandler クラス（リスト12）を作成します．

理ですが，この判断のための知識をカプセル化するこ

ShowHandlerとWriteHandlerクラスは，Application●リスト 11
図 3 ／ ApplicationHandlerクラスのメソッド呼び出し
ApplicationHandler
compute()do̲task()do̲create̲content()
do̲return̲content()●リスト 10$self->do̲task();$self->do̲create̲content();$self->do̲return̲content();

ApplicationHandler クラスの使用

ShowHandler クラス

package ShowHandler;use base 'ApplicationHandler';use strict;
sub do_create_content{
my $self = shift;
my $messages = $self->{message};$self->{content} = join "\n", 'In Show Mode', @$messages;}
1;__END__#!/usr/bin/perl●リスト 12use ApplicationHandler;use strict;
my $application = ApplicationHandler->new;$application->compute;__END__●リスト 13 ShowHandlerとWriteHandlerの使用

WriteHandler クラス

package WriteHandler;use base 'ApplicationHandler';use strict;
sub do_task{
my $self = shift;
my $query_strings = $self->{query};
my $messages = $self->{message};#!/usr/bin/perl
if ($query =˜ /new_message=(.*)$/) {push @$message, $1;}

use ShowHandler;
use WriteHandler;use strict;}

my $application;if ($ENV{QUERY_STRINGS} =˜ /mode=write/) {$application = WriteHandler->new;} else {$application = ShowHandler->new;}$application->compute;__END__

sub do_create_content{
my $self = shift;
my $messages = $self->{message};$self->{content} = join "\n", 'In Write Mode', @$messages;}
1;__END__WEB +DB PRESS Vol.6 ● 99Perl̲オブジェクト 04.1.21 20:22 ページ100

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介とで，さらに見通しが良くなるはずです．仮にその判スを変化させることができるために柔軟性が高まりま断の処理が複雑だったとしても，カプセル化してあれ

す．

ば利用する側は単にその機能を呼び出す手続きを知っているだけでよいのです．
そのための実装はリスト 14 のようになり，このApplicationFactory クラスはリスト15 のようなコード
で使用します．
結果このコードは，¡生成するクラスつまり実行する機能を決定する
ApplicationFactory クラス¡書き込みモードの動作を決定する

身 近 な例 では， R D B M S の A P I である D B I のconnect( )メソッドがFactory Methodパターンの実例と言えます．
ちなみにPerl はクラスとコンストラクタの関係にも動的束縛を使用することができるので，Factory
Method パターンを利用することなく生成するインス
タンスを変化させることができます．このクラスとコンストラクタの動的束縛を要する場合，インスタンス
化するクラスを文字列で指定します．

WriteHandler クラス

my $class_name = 'ShowHandler';¡読み出しモードの動作を決定する

my $application = $class_name->new;

ShowHandler クラス

この記述で，設定ファイルによってインスタンス化

といった風に明確に役割が分担され，条件分岐が少するクラスを指定するなどの高いカスタマイズ性を得ないため理解しやすい構成になっています．

ることができます．ただよほど単純な場面を除き，素このようにインスタンスを生成するための手続きを提供し，インスタンス化するクラスを決定するための直にFactory Method パターンを使用したほうが好ましいでしょう．

知識をカプセル化する構成をFactory Method パター
ンと呼びます．単純にコンストラクタを使用してインスタンスを生成する場合と比べ，Factory Method パ
ターンを利用した場合は，実際に生成するインスタン●リスト 14

ApplicationFactory クラス

package ApplicationFactory;
use WriteHandler;
use ShowHandler;use strict;
sub create{
if ($ENV{QUERY_STRINGS} =˜ /mode=write/) {return WriteHandler->new;} else {return ShowHandler->new;}}
1;__END__●リスト 15

ApplicationFactory クラスの使用#!/usr/bin/perl
use ApplicationFactory;
use strict;
my $application = ApplicationFactory->create;$application->compute;__END__

100● WEB +DB PRESS Vol.6●リスト 16

LoggingDecorator クラス

package LoggingDecorator;
use strict;
our $AUTOLOAD;
sub new{
my $class = shift;
my $component = shift;
bless { component => $component }, $class;}sub get_component{
my $self = shift;$self->{component};}
sub AUTOLOAD{
my $self = shift;
my @args = @_;my $method = $AUTOLOAD;$method =˜ s/.+://;
return if $method eq 'DESTROY';
printf STDERR "%s - %s->(%s)\n",scalar localtime,
ref$self->get_component,$method,join(', ', @args);$self->get_component->$method(@_);}
1;__END__

Perl̲オブジェクト 04.1.21 20:22 ページ101

特集 2が似合う人になろう

図 4 ／要求の転送
ClientLoggingDecorator

Component

AUTOLOAD()

method()$a̲component->method();

Decorator パターン

ニックで，これらの機能をまとめたDelegateモジュールなどがCPAN に登録されています．

アプリケーションの挙動を追跡するために詳細なト作成したLoggingDecorator クラスの使用方法を見

レースログを出力していると，デバッグの労力を大幅てみます．LoggingDecorator クラスは，コンストラに軽減できる場合があります．しかしログを出力する

クタの引数にトレースログ出力機能を追加したいオブ

ためのコードを各クラスに定義していると，本来の目

ジェクトを指定します（リスト17）．

的ではない処理が混入して簡潔性が失われてしまいま

生成したインスタンス$logging̲dbh はLogging

す．また継承しオーバライドしてログを出力するよう

Decorator クラスのインスタンスですが，受け取った

拡張すると，それぞれのクラスごとにログ出力用のサ要求をすべてDBIのオブジェクトに転送するため，普ブクラスが必要になってしまい，クラスの数が膨れ上通のDBI のデータベースハンドラと同じように振る舞がる恐れがあります．

います．そのためDBI のデータベースハンドラがもつ

デザインパターンのパターンカタログを見ると，目機能をそのままに，すべての操作に対してメソッド

的に「オブジェクトに責任を動的に追加する．サブク

名・引数・タイムスタンプをSTDERR に出力する拡ラス化よりも柔軟な機能拡張方法を提供する」と記張が加えられています．

されたDecoratorパターンが参考にできそうです．
Decorator パターンはオブジェクトを包み込み装飾

Mon Nov 19 17:38:03 2001 - DBI::db->prepare(
DELETE FROM users WHERE age < ?

（デコレーション）することで，そのオブジェクトに新しい機能を追加します．さらに包み込んだオブジェク
トに対して要求を転送するので，そのオブジェクトと同じように振る舞うことができます．これらの拡張を継承などの静的な手段ではなく，実行時に動的に機能を拡張できることがポイントです．

)
Mon Nov 19 17:38:04 2001 - DBI::db->commit()
Mon Nov 19 17:38:04 2001 - DBI::db->disconnect()

この例ではDBIのオブジェクトにロギング機能を追●リスト 17

LoggingDecorator クラスの使用

ここでは，リスト16のように任意のオブジェクトに#!/usr/bin/perl

トレースログ出力機能をデコレーションするLogging
Decoratorクラスを定義します．
LoggingDecorator クラスは，任意のオブジェクトにSTDERR へのトレースログ出力機能を追加します．
あるクラスで定義されていないメソッドを呼び出されたとき，Perl はAUTOLOAD メソッドを呼び出します． 呼 び出 そうとしていたメソッドは特 種 変 数$AUTOLOAD を参照することで得ることができ，それを元に本来その要求を受け取るべきオブジェクトに要求を転送することができます（図 4）．この方法はオブジェクトを自動転送する手段として一般的なテク

use LoggingDecorator;
use DBI;use strict;
my $dbh = DBI->connect('dbi:driver:dsn', '', '', {RaiseError => 1, AutoCommit => 0});
my $logging_dbh = LoggingDecorator->new($dbh);
eval {
my $state = $logging_dbh->prepare(q{DELETE FROM users WHERE age < ?
});$state->execute(18);$logging_dbh->commit;};$logging_dbh->rollback if $@;$logging_dbh->disconnect;__END__WEB +DB PRESS Vol.6 ● 101Perl̲オブジェクト 04.1.21 20:22 ページ102

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介加しましたが，他にもCGIモジュールやIO::Socketモ

要がないのです．また継承とは異なり，オブジェクト

ジュールなど，任意のオブジェクトにロギング機能をの振る舞いや責任を実行時に決定・変更できる強力な追加することができます．またget̲componentメソッ

柔軟性も見のがすことはできません．

ドで元のオブジェクトを取り出すこともできるので，任意のタイミングでこの機能を取り外すことも可能です．

Command パターンWeb アプリケーションは，いかに資源の占有時間■ホワイトボックスな再利用を短くするか，試行錯誤の連続と言えます．特にフロ継承は，継承するクラスの内部構造に関する知識

ントエンドのApache は利用できるスロットの数が限をある程度要求するため，ホワイトボックスな再利用られているため，長時間スロットを占有するようなアと呼ばれます．つまり継承関係を持つことはそのクラプリケーションでは新たな要求をさばくためのスロッ

スどうしが非常に強い結びつきを持つことであり，ベトが確保できず，全体的な待ち時間が増大してしまいースクラスの変更がサブクラスへ直接影響する危険性ます．

があります．これはベースクラスの一部を変更すればこの状況を回避する手段として処理時間を短縮する

すべてのサブクラスに対して反映されるメリットの裏ことが基本として挙げられますが，これにも限界があ

返しのデメリットと言えます．

ります．そのため，大量のトラフィックをさばくWeb
アプリケーションでは，処理の要求を一時的に保存し■ブラックボックスな再利用て別のタイミングで処理を実行する非同期処理を行う

逆にDecorator パターンなどのように委譲によって場合もあります．非同期処理を実現するための方法はオブジェクトの機能を拡張・変更してゆく方法は，ブ

いくつか存在しますが，ここでは手軽なキューを利用

ラックボックスな再利用と呼ばれます．ブラックボッ

した非同期処理を実装します．

クスな再利用はあるオブジェクトの振る舞いを拡張しデザインパターンのパターンカタログを眺めると，たい場合でも，そのオブジェクトの内部構造を知る必目的に「要求をオブジェクトとしてカプセル化するこ●リスト 18

SendmailCommand クラス

package SendmailCommand;use base 'Command';use strict;use constant MAIL_TEMPLATE => <<__MAIL_TEMPLATE__;
From: <%>
To: <%s>
Subject: %s%s__MAIL_TEMPLATE__
sub to{my $self = shift;$self->{to} = shift if @_;$self->{to};}sub execute{
my $self = shift;open MAIL, '| /usr/lib/sendmail -f info@example.jp';
printf MAIL MAIL_TEMPLATE,'info@example.jp', $self->to, 'Demo Message', 'Hello World';
close MAIL;}
1;__END__

102● WEB +DB PRESS Vol.6とによって，異なる要求や，要求からなるキューやログによりクライアントをパラメータ化する．また，取り消し可能なオペレーションをサポートする」を掲げるCommand パターンがこの例に適切であることがわかります．Command パターンを参考にしたキューの実装Queue クラスとCommand クラスをhttp://perl.
図 5 ／ Queue クラスの動作
objectobjectobjectobjectQueuePerl̲オブジェクト 04.1.21 20:22 ページ103

特集 2が似合う人になろう

infoware.ne.jp/source/Queue-0.1.tar.gz からダウンロ

す．ここでもポリモフィズムを有効活用することで条ードできるので今回はこれを題材に説明します．

件分岐が不要になることがわかります．

Queueクラスはファイルを使用したキューの実装で，この例では投入済みのCommand オブジェクトをす

任意のオブジェクトをキューに蓄積することができま

べてキューから取り出して実行していますが，サーバ

す．QueueクラスはStorableモジュールを使用してオの負荷に応じて一定量以上同時に処理しないようにすブジェクトを文字列に変換してファイルに格納し，取ることも可能です．さらにネットワークを経て共有でり出すときはファイルの文字列を評価してオブジェク

きるキューを利用した場合は，Command オブジェク

トを復元します（図5）．

トの実行を複数のマシンで分散処理することも可能でキューに投入する処理の定義は，Commandクラス

す．要求をするプロセスと要求を処理するプロセスをのサブクラスで定義します．メール送信処理を行う

分断することで，応答性の確保ばかりでなく処理能力

SendmailCommand クラス（リスト18）を定義してのスケーラビリティも同時に得ることができるのです．

みます．
SendmailCommandクラスは，toメソッドで送信先

Singleton パターンをセットした後にexecute メソッドを実行するとメー

設定ファイルを参照することで，アプリケーション

ルの送信を始めます．単体で利用することもできます

が出力するメッセージや使用するクラスを変更するこが，キューを使用する場合は送信先を設定した状態でとが可能になり，柔軟なソフトウェアができあがりま

キューに投入します（リスト19）．

す．ところが設定ファイルを参照するために複数のク

Webアプリケーション側ではこの操作で終了し，次ラスで個別に設定ファイルの読み込み処理を実装したの要求に備えます．今度はこの要求をキューから取りり，個々に設定ファイルを読み込むインスタンスを生出して実行する別のプロセスを定義します（リスト20）．

成していたりしては無駄が多くなります．もちろんグ

このコードを起 動 するとキューからすべての

ローバル変数にそのインスタンスを保持して使い回す

Command オブジェクト順番に取り出しexecute メソ

方法も採れますが，横断的に利用されるグローバル変ッドを呼び出して逐一実行します．Perl のref 関数で数はコードの理解の妨げになり，保守上の障害になる取得したCommand オブジェクトのクラス名を元にク可能性も高いため，できるだけ利用は避けたいものでラスを動的にロードしています．

す．

この例では，キューに投入され$command へ取りパターンカタログの目的に「あるクラスに対してイ

出されるオブジェクトはSendmailCommandクラスのンスタンスが1 つしか存在しないことを保証し，それインスタンスだけですが，CreateNewUserAccountにアクセスするためのグローバルな方法を提供する」

Command クラスなど他のオブジェクトがキューに投を掲げるSingleton パターンが参考になりそうなので，入されていたとしても，execute メソッドを実装した
オブジェクトであれば問題なく実行することができま●リスト 20●リスト 19#!/usr/bin/perluse Queue;
use strict;SendmailCommand クラスの使用（クライアント側）#!/usr/bin/perluse Queue;
use SendmailTask;
use strict;
my $command = SendmailTask->new;$command->to('oyama@crayfish.co.jp');
my $queue = Queue->new('/path/to/directory');$queue->add($command);__END__

SendmailCommand クラスの使用（サーバ側）

my $queue = Queue->new('/path/to/directory');
while (my $command = $queue->get) {eval {
my $class = ref $command;
eval "require $class";$command->execute;};print STDERR $@ if $@;}__END__WEB +DB PRESS Vol.6 ● 103

Perl̲オブジェクト 04.1.21 20:22 ページ104

第3 章 デザインパターンを学ぶ
オブジェクト指向と頻出パターンの紹介●リスト 21SingletonConf クラス●リスト 22

SingletonConf クラスの使用

package SingletonConf;use base 'Conf';
use strict;#!/usr/bin/perl
use strict;use SingletonConf;

our $Instance;
sub instance{
my $class = shift;
return $Instance if defined $Instance;

my $conf = SingletonConf->instance;
my $class_name = $conf->get('DEFAULT_LOGGING_CLASS');
my $log = $class_name->new;__END__$Instance = $class->new(@_);
return $Instance;}

ブジェクト指向システムに慣れ親しんだ人であれば，パターンによって設計を洗練させ知識を整理し，人に1;__END__

伝えることが容易になるはずです．もしあなたがオブ
ジェクト指向システムに触れて間もない人ならば，パこのパターンを利用して無駄を省いてみましょう．
Singletonパターンを実装する方法はいくつかありま

ターンから良い設計や定石を学びとることができるはずです．

すが，今回は最も手軽なクラス変数を利用した実装を今回紹介したのはパターンのごく一部で，この他に行います．すでに設定ファイルを読み込むConf クラも有用なパターンがたくさん公開されています．ソフスが存在するとします．そのクラスを基にインスタン

トウェアの開発に携わる人はぜひパターンに触れて，スを1つだけ生成する拡張をします（リスト21）．

見つけて，そして共有してみてください．それはあな作成したSingletonConfクラスには，新しくinstance
メソッドを追加しました． instance メソッドは，

たとあなたたちのソフトウェア開発経験をさらに豊かにしてくれるはずです．#

SingletonConf クラスのインスタンスがすでに生成されているときはその生成済みのインスタンスを返し，

参考文献

生成されていないときは新しくインスタンスを生成し¡『オブジェクト指向における再利用のためのデザイて返します．

ンパターン―改訂版』

これによって設定ファイルの情報を参照したい場所

ISBN4-7973-1112-6，ソフトバンクパブリッシング，

ではリスト22のように，決まった文法でインスタンス

Erich Gamma/Richard Helm/Ralph Johnson/John

化して参照することができるようになります．Singleton

Vlissides 著，本位田真一／吉田和樹 監訳

パターンを利用することで生成するConfクラスのイン¡『リファクタリング』

スタンスを1 つだけに抑えることができたうえ，グロISBN4-89471-228-8，ピアソン・エデュケーション，

ーバル変数に頼る必要もなくなります．

Martin Fowler 著，児玉公信／友野晶夫／平澤章／梅澤真史訳

定石という宝石¡『実用Perl プログラミング』
ISBN4-900900-82-6，オライリージャパン，Sriram

デザインパターンは，ソフトウェア設計の定石を集

Srinivasan 著，須田隆久訳めたものだと冒頭で述べました．ソフトウェアの世界¡「Tom's object-oriented tutorial for perl」

も，たとえば囲碁の世界と同様で，定石を外した設計

http://www.perldoc.com/perl5.6.1/pod/perltoot.html，

は素人相手には通用するかもしれませんが，プロにはTom Christiansen 著

通用しません．

¡「Object-oriented with Perl メーリングリスト」

CPAN などのライブラリを活用することと同様に，

http://perl.infoware.ne.jp/

自分の取り組んでいる分野がすでにパターン化されて¡perldoc

いないか，まず調べてみてください．もしあなたがオ% perldoc perl

104● WEB +DB PRESS Vol.6