ぐらめぬ・ぜぷつぇんのはてダ(2007 to 2011)

2007年~2011年ごろまで はてなダイアリー に書いてた記事を引っ越してきました。

Factoryが内部で保持するキャッシュを回避する方法を思いついた。

PHPならではの荒技で、$GLOBALSに「ゾーン」を指示する変数を保持して、Factory内部のキャッシュはゾーン毎に保持させる。

PHPならではのキショイコード、行きます。

define('FACTORY_ZONE', "\0__GLOBAL_FACTORY_ZONE_INDICATOR");

// 初期ゾーン
$GLOBALS[FACTORY_ZONE] = 'default';

で、内部でオブジェクトをキャッシュするfactoryの疑似コードは・・・

【ゾーン対応前】

function &foobar_factory($name)
{
    $_n = ucwords(strtolower($name));
    static $instances = array();
    if (isset($instances[$_n])) {
        return $instances[$_n];
    }
    $_klass = 'foo_bar_package_' . $_n;
    if (!class_exists($_klass)) {
        $_require = str_replace('_', '/', $_klass) . '.php';       
        require_once($_require);
    }

    // コンストラクタパラメータは設定ファクトリから$nameで取得
    $params = foobar_Config::getInitParams($name);

    $instances[$_n] = new $_klass($params);
    return $instances[$_n];
}

【ゾーン対応後】

function &foobar_factory($name)
{
    $_zone = $GLOBALS[FACTORY_ZONE];

    $_n = ucwords(strtolower($name));
    static $instances = array();
    if (isset($instances[$_zone][$_n])) {
        return $instances[$_zone][$_n];
    }
    $_klass = 'foo_bar_package_' . $_n;
    if (!class_exists($_klass)) {
        $_require = str_replace('_', '/', $_klass) . '.php';       
        require_once($_require);
    }

    $params = foobar_Config::getInitParams($name);

    $instances[$_zone][$_n] = new $_klass($params);
    return $instances[$_zone][$_n];
}

foobar_Config::getInitParams()の中身もzone対応が必要だが、これは実装寄りになってくるのでここでの説明は省略する。
まぁiniファイルなりpropertyファイルなり(それはJavaか)DBなりをzoneで区別してget/setできるようになっているものとする。

では、これを用いてテスト用のコードを考えてみよう。PHPUnitなどのxUnitを用いる場合、かなりの数のゾーンが必要になってくる。ようするにこの場合の「ゾーン」というのは、とどのつまりはDI設定ファイルみたいな代物になってくる・・・のかな?えっと、つまり生成オブジェクトをキャッシュしてしまうファクトリを「騙す」というか「仕切りを設ける」ための仕掛けなので、テストケース毎に仕切る必要が・・・えと、YakiBikiでは出てくるんですよ!ええ!

こんな感じかな?

function test_foobar_TestCase()
{
    $test_zone = mt_rand();
    $bk = $GLOBALS[FACTORY_ZONE];
    $GLOBALS[FACTORY_ZONE] = $test_zone;
    foobar_Config::setInitParams('objname1', array(...));

    $o =& foobar_factory('objname1');

    //...

    $GLOBALS[FACTORY_ZONE] = $bk;
}

って感じにすれば、テストケース毎に完全にfactoryキャッシュを分けてオブジェクトが生成されるので、悩みどころも解決するはず・・・!!*1

ビールサーバの樽の調達ができたので、明日予定通り開催します!ということで、9/30(火)のもくもく会@ディノはビールパーティですよ。
(省略)
・俺の話を聞けー!というのは大歓迎です。

2008-09-29 - kunitの日記

というわけで、kunitさん→こりずに明日も襲撃します。実際のYakiBikiで該当する箇所を、その場で↑こんな感じに直してみますので見てもらっていいっすか!?

*1:多分、DIコンテナを使っている場合はDIの設定ファイルを切り分けるか、そもそも生成オブジェクトをキャッシュせず毎回newさせるような設定にしてテストケース用の設定ファイルを作れば問題ないはず。・・・多分?Springを以前ちょこっと仕事で使った時にざっと調べた限りの偏見で物言ってますが。