&reftitle.examples;
オブジェクトの集約の例 関連は、独立に構築され、外部から 可視の部分を合成したものです。クラスまたはオブジェクトを 関連づける場合、各クラスは関連するクラスへのリファレンスを保持します。 複数のクラスを静的に関連づける場合は、クラスは他のクラスの インスタンスへのリファレンスを含みます。例えば、 クラスの関連付け _dt = new MyDateTime(); // 初期化コード ... } function generateReport() { $dateTime = $this->_dt->now(); // その他のコード ... } // その他のメソッド ... } $rep = new Report(); ?> ]]> コンストラクタ (または他のメソッド) にリファレンスを渡すことにより 実行時に複数のインスタンスを関連づけることも可能です。 これにより、複数のオブジェクト間の関連を動的に変更することが 可能です。この例を示すために上の例を変更してみます。 オブジェクトの関連 _format = $format; } function now() { return date($this->_format); } } class Report { var $_dt; // MyDateTime へのリファレンスをここに保持 // その他のプロパティ ... function Report() { // 初期化を行う } function setMyDateTime(&$dt) { $this->_dt =& $dt; } function generateReport() { $dateTime = $this->_dt->now(); // その他のコード ... } // その他のメソッド ... } $rep = new Report(); $dt = new MyDateTime(); $dtp = new MyDateTimePlus("l, F j, Y (h:i:s a, T)"); // Web 表示用に簡単な日付を付けたレポートを生成する $rep->setMyDateTime($dt); echo $rep->generateReport(); // その他のコード ... // かっこの良いレポートを生成する $rep->setMyDateTime($dtp); $output = $rep->generateReport(); // データベースに $output を保存 // ... 等 ... ?> ]]> 一方、集約では、合成されたパーツのカプセル化 (隠蔽) が行われます。(静的な) 内部クラス (PHP はまだ内部クラスを サポートしていません) を使用することにより、クラスを集約することが できます。この場合、このクラスを含むクラスを通じる場合以外、集約された クラスの定義にはアクセスできません。複数のインスタンスの集約 (オブジェクト集約) は、あるオブジェクトの内部にサブオブジェクトを 動的に作成することを意味し、この過程でこのオブジェクトのプロパティと メソッドを拡張します。 オブジェクトの集約は、(例えば、分子は原子を集約したものであるといった) 包含関係を表す際の自然な方法であり、サブクラスを複数の親クラス およびそのインターフェイスに永続的にバインドすることなく、 多重継承と等価な機能を得るために使用できます。 実際、オブジェクトの集約はより柔軟に使用することができ、集約される オブジェクトで継承するメソッドまたはプロパティを選択することが できます。
&reftitle.examples; 3 つのクラスを定義し、各々に別々のストレージメソッドを実装します。 storage_classes.inc data = $data; } function write($name) { $fp = fopen(name, "w"); fwrite($fp, $this->data); fclose($data); } } class WDDXStorage { var $data; var $version = "1.0"; var $_id; // "private" 変数 function WDDXStorage($data) { $this->data = $data; $this->_id = $this->_genID(); } function store() { if ($this->_id) { $pid = wddx_packet_start($this->_id); wddx_add_vars($pid, "this->data"); $packet = wddx_packet_end($pid); } else { $packet = wddx_serialize_value($this->data); } $dbh = dba_open("varstore", "w", "gdbm"); dba_insert(md5(uniqid("", true)), $packet, $dbh); dba_close($dbh); } // プライベートメソッド function _genID() { return md5(uniqid(rand(), true)); } } class DBStorage { var $data; var $dbtype = "mysql"; function DBStorage($data) { $this->data = $data; } function save() { $dbh = mysql_connect(); mysql_select_db("storage", $dbh); $serdata = serialize($this->data); mysql_query("insert into vars ('$serdata',now())", $dbh); mysql_close($dbh); } } ?> ]]> この定義済みクラスを用いていくつかのオブジェクトをインスタンス化し、 集約や集約の解除を行いつつ随時オブジェクトの情報を出力します。 test_aggregation.php $v) $out[] = "\t$k => $v"; return implode("\n", $out); } function object_info($obj) { $out[] = "クラス: " . get_class($obj); foreach (get_object_vars($obj) as $var=>$val) { if (is_array($val)) { $out[] = "プロパティ: $var (array)\n" . p_arr($val); } else { $out[] = "プロパティ: $var = $val"; } } foreach (get_class_methods($obj) as $method) { $out[] = "メソッド: $method"; } return implode("\n", $out); } $data = array(M_PI, "kludge != cruft"); // 基本オブジェクトを作成する $fs = new FileStorage($data); $ws = new WDDXStorage($data); // オブジェクトの情報を表示する echo "\$fs オブジェクト\n"; echo object_info($fs) . "\n"; echo "\n\$ws オブジェクト\n"; echo object_info($ws) . "\n"; // 集約を行う echo "\n\$fs を WDDXStorage クラスに集約します\n"; aggregate($fs, "WDDXStorage"); echo "\$fs オブジェクト\n"; echo object_info($fs) . "\n"; echo "\nそれを DBStorage クラスに集約します\n"; aggregate($fs, "DBStorage"); echo "\$fs オブジェクト\n"; echo object_info($fs) . "\n"; echo "\nWDDXStorage を集約から解除します\n"; deaggregate($fs, "WDDXStorage"); echo "\$fs オブジェクト\n"; echo object_info($fs) . "\n"; ?> ]]> 出力内容を見ながら、PHP の集約についての副作用や制限事項を 考えてみましょう。 まず、新しく作成されたオブジェクト $fs および $ws は、期待通りの (対応するクラス定義に もとづく) 結果を出力します。 PHP では実際のところクラス/オブジェクトの要素についてパブリック/ プライベートの区別はありませんが、集約の際には クラス/オブジェクトのプライベート要素はアンダースコア文字 ("_") で始まるとみなします。 3.1415926535898 1 => kludge != cruft メソッド: filestorage メソッド: write $ws オブジェクト クラス: wddxstorage プロパティ: data (array) 0 => 3.1415926535898 1 => kludge != cruft プロパティ: version = 1.0 プロパティ: _id = ID::9bb2b640764d4370eb04808af8b076a5 メソッド: wddxstorage メソッド: store メソッド: _genid ]]> 次に $fsWDDXStorage クラスと集約し、オブジェクトの情報を出力します。 $fs は今でも FileStorage のままですが、プロパティ $version およびメソッド store が存在することがわかるでしょう。これらは いずれも WDDXStorage で定義されているものです。 注意すべき点は、クラスで定義されているプライベート要素は集約されていない ということです。それらは $ws オブジェクトの中には 存在します。また、WDDXStorage のコンストラクタも 存在しません。これを集約するのは論理的ではありません。 3.1415926535898 1 => kludge != cruft プロパティ: version = 1.0 メソッド: filestorage メソッド: write メソッド: store ]]> 集約処理は、積み重ねていくことが可能です。そこで今度は $fsDBStorage を 集約します。これにより、定義されているすべてのクラスの 保存処理メソッドを使用可能なオブジェクトができあがります。 3.1415926535898 1 => kludge != cruft プロパティ: version = 1.0 プロパティ: dbtype = mysql メソッド: filestorage メソッド: write メソッド: store メソッド: save ]]> 最後に、プロパティやメソッドを動的に集約したのと同じ方法で、 集約したプロパティやメソッドを解除することも可能です。 $fs から WDDXStorage クラスの集約を解除すると、このような結果になります。 3.1415926535898 1 => kludge != cruft プロパティ: dbtype = mysql メソッド: filestorage メソッド: write メソッド: save ]]> 上で説明しきれなかったことがひとつあります。それは、集約処理の際には 既存のプロパティやメソッドは上書きされないということです。例えば、 FileStorage クラスでは $data というプロパティを定義しており、 WDDXStorage クラスでも同名のプロパティが 定義されていますが、このプロパティが FileStorage のインスタンス化の際に 取得したプロパティの内容を上書きすることはありません。