|
zip形式
tgz形式
オブジェクト指向についてはまだまだ勉強中ですが、「PHPでGoFのデザインパターンを実装したら、どんな感じになるんだろ?」と思ってしまったので、ちょっとずつやってみることにしました。
間違いやご意見がありましたら、遠慮なくツッコミを入れてくださいm(_"_)m
今回は、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
class Selector
{
function setCode($code) { Abstract::set("setCode"); }
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;
}
}
class EmpProxy extends Selector
{
var $code_;
var $name_;
var $selector_;
function EmpProxy()
{
echo "EmpProxyがインスタンス化されました<br>";
$this->code_ = "";
$this->name_ = "";
$this->selector_ = null;
}
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();
}
function _makeSelector()
{
if ($this->selector_ == null) {
echo "EmpSelectorをインスタンス化します<br>";
$this->selector_ = new EmpSelector();
$this->selector_->setCode($this->code_);
$this->selector_->setName($this->name_);
}
}
}
?>
<?php
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パターン」といったところでしょうか?
|