Do You PHP?    
Search Engine Optimization  php5 powerd  Valid XHTML 1.0!  Valid CSS!  このサイトのはてなブックマーク数 



last updated
2005/05/25

counter hits
since 1999/11/06


PHP4でデザインパターン(Proxy編)

zip形式 tgz形式

オブジェクト指向についてはまだまだ勉強中ですが、「PHPでGoFのデザインパターンを実装したら、どんな感じになるんだろ?」と思ってしまったので、ちょっとずつやってみることにしました。

caution間違いやご意見がありましたら、遠慮なくツッコミを入れてくださいm(_"_)m

今回は、Proxyパターンで「必要なときになって初めてインスタンスを作成する」パターンです(以下のクラス図を参照)。

Proxyパターンのクラス図

今回のサンプルは、Oracleのサンプルテーブルであるemp表に格納された従業員データから、従業員番号(emp.empno)・氏名(emp.ename)を検索条件として前方一致で検索するクラス(EmpSelectorクラス)、また、そのProxyクラス(EmpProxyクラス)を使って、Proxyを使用する・しない場合の内部的な動きを示してみます。

以下がコードで、Abstractクラスなどを含めたスクリプトコードは、上のリンクからダウンロードできます。動作確認はPHP4.2.2です。

●Proxy.php

<?php
require_once("jp/ne/hi_ho/pat/dimension/Abstract.phl");
?>
<?php
/**
 * APIの定義
 */
class Selector
{
    // 条件としてempnoをセット
    function setCode($code) { Abstract::set("setCode"); }

    // 条件としてenameをセット
    function setName($name) { Abstract::set("setName"); }

    // 結果を返す
    function getResult() { Abstract::set("getResult"); }
}

/**
 * 実体の定義
 */
class EmpSelector extends Selector
{
    var $code_;
    var $name_;

    function EmpSelector()
    {
        echo "EmpSelectorがインスタンス化されました<br>";
        $this->code_ = "";
        $this->name_ = "";
    }
    function setCode($code) { $this->code_ = $code; }
    function setName($name) { $this->name_ = $name; }
    function getResult()
    {
        $ret = array();
        $i = 0;

        $dbc = OCILogon("scott", "tiger", "orcl");
        $sql = "SELECT ename FROM emp "
             . "WHERE empno LIKE :code "
             . "AND ename LIKE :name "
             . "ORDER BY empno ";
        $stmt = OCIParse($dbc, $sql);

        $cd = $this->code_ . "%";
        $nm = $this->name_ . "%";

        OCIBindByName($stmt, ":CODE", &$cd, -1);
        OCIBindByName($stmt, ":NAME", &$nm, -1);
        OCIExecute($stmt, OCI_DEFAULT);

        while(OCIFetch($stmt))
        {
            $ret[$i++] = OCIResult($stmt, "ENAME");
        }

        OCIFreeStatement($stmt);
        OCILogOff($dbc);

        return $ret;
    }
}

/**
 * Proxyの定義
 */
class EmpProxy extends Selector
{
    var $code_;
    var $name_;
    var $selector_;

    function EmpProxy()
    {
        echo "EmpProxyがインスタンス化されました<br>";
        $this->code_ = "";
        $this->name_ = "";
        $this->selector_ = null;
    }

    /**
     * 基本的にここではproxy内に溜めておくだけ。
     * 実体をすでに作成していれば、そちらも処理を行う
     */
    function setCode($code)
    {
        echo "条件としてempnoをセットします<br>";
        if ($this->selector_ != null) {
            echo "EmpSelectorは既にインスタンス化されています<br>";
            $this->selector_->setCode($code);
        }
        $this->code_ = $code;
    }
    function setName($name)
    {
        echo "条件としてenameをセットします<br>";
        if ($this->selector_ != null) {
            echo "EmpSelectorは既にインスタンス化されています<br>";
            $this->selector_->setName($name);
        }
        $this->name_ = $name;
    }
    function getResult()
    {
        echo "結果を取得します<br>";
        // ようやく実体を生成する
        $this->_makeSelector();
        return $this->selector_->getResult();
    }

    /**
     * privateなメソッド
     */
    function _makeSelector()
    {
        if ($this->selector_ == null) {
            echo "EmpSelectorをインスタンス化します<br>";
            $this->selector_ = new EmpSelector();
            $this->selector_->setCode($this->code_);
            $this->selector_->setName($this->name_);
        }
    }
}
?>
<?php
    /**
     * main
     */
    echo "●EmpSelectorをそのまま呼び出してみる<br>";
    $selector = new EmpSelector();
    $selector->setCode("75");
    echo "<pre>";
    print_r($selector->getResult());
    echo "</pre>";
    $selector->setCode("77");
    $selector->setName("S");
    echo "<pre>";
    print_r($selector->getResult());
    echo "</pre>";

    echo "●こんどは、Proxy経由で呼び出してみる<br>";
    $proxy = new EmpProxy();
    $proxy->setCode("75");
    echo "<pre>";
    print_r($proxy->getResult());
    echo "</pre>";
    $proxy->setCode("77");
    $proxy->setName("S");
    echo "<pre>";
    print_r($proxy->getResult());
    echo "</pre>";
?>

●実行結果
●EmpSelectorをそのまま呼び出してみる
EmpSelectorがインスタンス化されました

Array
(
    [0] => WARD
    [1] => JONES
)

Array
(
    [0] => SCOTT
)

●こんどは、Proxy経由で呼び出してみる
EmpProxyがインスタンス化されました
条件としてempnoをセットします

結果を取得します
EmpSelectorをインスタンス化します
EmpSelectorがインスタンス化されました
Array
(
    [0] => WARD
    [1] => JONES
)

条件としてempnoをセットします
EmpSelectorは既にインスタンス化されています
条件としてenameをセットします
EmpSelectorは既にインスタンス化されています

結果を取得します
Array
(
    [0] => SCOTT
)
  

実行結果からもお分かりのように、EmpProxyオブジェクトが実際にEmpSelectorクラスをインスタンス化するのはgetProxyメソッドを呼び出したときになっています。こうすることで、例えば初期化処理に時間がかかるクラスなどでタイミングをずらすことが可能になります(アプリケーションの起動時にレスポンスが悪いよりは良いと思われます)。

しかし、EmpSelectorで実装している「遅延評価」を考えると、「なぜ、proxyが必要か?」と思ってしまいますが、これはAPIを定義しているSelectorクラス(interface)を継承して実装するメソッドのうち、Proxyが実体に処理を任せる(丸投げ?)のはどれなのか選択することが、EmpSelectorクラスのコードを変更することなくできるようになります。

イメージとしては、「APIが全く同じ場合のAdapterパターン」といったところでしょうか?



About This Site |  Privacy Policy |  Contact
Copyright © 1999 - 2005 by Hideyuki SHIMOOKA all rights reserved.