北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

mod_perlとCGI

例えばHTTPのリクエストを表すクラスを作る際、mod_perlではApache::Request、CGIではCGI.pmに処理を委譲したいと思ったとき、コンストラクタさえ変えれば他のメソッドはほぼ同じなので、わざわざファクトリメソッドを作ったりしたくない。そこでこんな方法はどうだろう?

package Hiratara::Request;
use strict;

BEGIN{
no strict qw{refs};
if($ENV{MOD_PERL}){
require Apache;
require Apache::Request;
import Apache::Request;
*{__PACKAGE__ . '::new'} = ¥&_mod_perl_new;
}else{
require CGI;
import CGI;
*{__PACKAGE__ . '::new'} = ¥&_cgi_new;
}
}

sub _mod_perl_new{
bless {delegate => Apache::Request->new(Apache->request())}, shift;
}

sub _cgi_new{
bless {delegete => CGI->new()}, shift;
}

...

1;

これだと、リクエストごとにmod_perlCGIかを判断するオーバーヘッドもなく、コードも割とすっきりする。ただし、分岐が多くなると先頭の関数定義がやたら増えるので、素直にファクトリメソッドでクラスごと切り替えた方が良さそうだ。

後、この方法はApache::StatINCを使っている時にコードに修正を加えた場合に、不安定な動作をする。つまり、mod_perl下で開発中の変更の多い部分に用いると痛い目にあう。理由は、先頭のシンボルテーブルの書き換え部分では新しい_mod_perl_new (_cgi_new)のコンパイルが終わっていないため、古いコードリファレンスに紐づけられることがあるからである。

[2005/12/21 補足]
開発中は、

 *{__PACKAGE__ . '::new'} = sub {&_mod_perl_new(@_)};

と書くといいでしょう。