<?php インターフェイス IUser { 関数 getName(); } ユーザーが IUser を実装するクラス { パブリック関数 __construct( $id ) { } パブリック関数 getName() { 「ジャック」を返します。 } } クラスUserFactory { パブリック静的関数 Create( $id ) { 新しいユーザー( $id )を返します; } } $uo = UserFactory::Create( 1 ); echo( $uo->getName()."n" ); ?> |
図 1. ファクトリ クラスとそれに関連する IUser インターフェイスとユーザー クラス |
%phpfactory1.php ジャック % |
<?php インターフェイス IUser { 関数 getName(); } ユーザーが IUser を実装するクラス { パブリック静的関数 Load( $id ) { 新しいユーザー( $id )を返します; } パブリック静的関数 Create( ) { 新しいユーザーを返します( null ); } パブリック関数 __construct( $id ) { } パブリック関数 getName() { 「ジャック」を返します。 } } $uo = ユーザー::Load( 1 ); echo( $uo->getName()."n" ); ?> |
図 2. ファクトリ メソッドを備えた IUser インターフェイスとユーザー クラス |
%phpfactory2.php ジャック % |
上で述べたように、小規模な環境では、このようなモードは過剰に見える場合があります。ただし、あらゆる規模のプロジェクトに適用できる、この堅実なコーディング形式を学ぶことが最善です。
単一要素モード
一部のアプリケーション リソースは、このタイプのリソースが 1 つしかないため、排他的になります。たとえば、データベース ハンドルを介したデータベースへの接続は排他的です。アプリケーション全体でデータベース ハンドルを共有する必要があるのは、接続を開いたままにするか閉じたままにするときにオーバーヘッドが発生するためであり、単一ページをフェッチするプロセスではさらにオーバーヘッドがかかるからです。
単一要素モードはこの要件を満たします。アプリケーションに一度に 1 つのオブジェクトだけが含まれる場合、このオブジェクトはシングルトンです。リスト 3 のコードは、PHP V5 のデータベース接続の単一要素を示しています。
リスト 3. Singleton.php
<?php require_once("DB.php"); クラスデータベース接続 { パブリック静的関数 get() { 静的 $db = null; if ( $db == null ) $db = 新しいデータベース接続(); $db を返します。 } プライベート $_handle = null; プライベート関数 __construct() { $dsn = 'mysql://root:パスワード@localhost/photos'; $this->_handle =& DB::Connect( $dsn, array() ); } パブリック関数ハンドル() { $this->_handle を返します。 } } print( "Handle = ".DatabaseConnection::get()->handle()."n" ); print( "Handle = ".DatabaseConnection::get()->handle()."n" ); ?> |
図 3. データベース接続の単一要素 |
% php シングルトン.php ハンドル = オブジェクト ID #3 ハンドル = オブジェクト ID #3 % |
オブザーバーパターン
Observer パターンは、コンポーネント間の密結合を回避する別の方法を提供します。パターンは非常に単純です。オブジェクトは、別のオブジェクト (オブザーバー) が自分自身を登録できるようにするメソッドを追加することで、自分自身を監視可能にします。監視可能なオブジェクトが変化すると、登録されたオブザーバーにメッセージが送信されます。これらのオブザーバーは、この情報を使用して、監視可能なオブジェクトとは独立して操作を実行します。その結果、オブジェクトは理由を理解することなく相互に通信できるようになります。 簡単な例は、システム内のユーザーのリストです。リスト 4 のコードはユーザーのリストを表示し、ユーザーが追加されるとメッセージを送信します。このリストは、ユーザーが追加されたときにメッセージを送信するログ オブザーバーを介して監視できます。
リスト 4. Observer.php
<?php インターフェース IObserver { 関数 onChanged( $sender, $args ); } インターフェース IObservable { 関数 addObserver( $observer ); } クラス UserList は IObservable を実装します { プライベート $_observers = array(); パブリック関数 addCustomer( $name ) { foreach( $this->_observers as $obs ) $obs->onChanged( $this, $name ); } パブリック関数 addObserver( $observer ) { $this->_observers []= $observer; } } クラス UserListLogger は IObserver を実装します { パブリック関数 onChanged( $sender, $args ) { echo( "'$args' がユーザー リストに追加されましたn" ); } } $ul = 新しいユーザーリスト(); $ul->addObserver( new UserListLogger() ); $ul->addCustomer( "ジャック" ); ?> |
図 4. 監視可能なユーザー リストとユーザー リスト イベント ロガー |
% php オブザーバー.php 「ジャック」がユーザーリストに追加されました % |
一連のコマンドモード
コマンド チェーン パターンは疎結合トピックに基づいており、一連のハンドラーを通じてメッセージ、コマンド、リクエストなどを送信します。各ハンドラーは、リクエストを処理できるかどうかを独自に判断します。可能な場合、リクエストは処理され、プロセスは停止します。他のハンドラーに影響を与えることなく、システムにハンドラーを追加または削除できます。リスト 5 は、このパターンの例を示しています。
リスト 5. Chain.php
<?php インターフェースIコマンド { 関数 onCommand( $name, $args ); } クラスコマンドチェーン { プライベート $_commands = array(); パブリック関数 addCommand( $cmd ) { $this->_commands []= $cmd; } パブリック関数 runCommand( $name, $args ) { foreach( $this->_commands as $cmd ) { if ( $cmd->onCommand( $name, $args ) ) 戻る; } } } クラス UserCommand は ICommand を実装します { パブリック関数 onCommand( $name, $args ) { if ( $name != 'addUser' ) は false を返します。 echo( "UserCommand が 'addUser' を処理していますn" ); true を返します。 } } クラス MailCommand は ICommand を実装します { パブリック関数 onCommand( $name, $args ) { if ( $name != 'mail' ) は false を返します。 echo( "'mail' を処理する MailCommandn" ); true を返します。 } } $cc = 新しいコマンドチェーン(); $cc->addCommand( new UserCommand() ); $cc->addCommand( new MailCommand() ); $cc->runCommand( 'addUser', null ); $cc->runCommand( 'メール', null ); ?> |
図 5. コマンド チェーンとその関連コマンド |
%phpchain.php 「addUser」を処理する UserCommand 「メール」を処理する MailCommand % |
戦略パターン
最後に取り上げるデザイン パターンは、戦略パターンです。このパターンでは、アルゴリズムが複雑なクラスから抽出されるため、簡単に置き換えることができます。たとえば、検索エンジンでのページのランク付け方法を変更したい場合は、戦略モードが適しています。検索エンジンの各部分、つまりページを横断する部分、各ページをランク付けする部分、およびランキングに基づいて結果を並べ替える部分について考えてみましょう。複雑な例では、これらのパーツはすべて同じクラスに属します。 Strategy パターンを使用すると、配置部分を別のクラスに配置して、検索エンジンのコードの残りの部分に影響を与えることなく、ページの配置方法を変更できます。
より簡単な例として、リスト 6 に、一連のプラグ アンド プレイ ポリシーに基づいて一連のユーザーを検索する方法を提供するユーザー リスト クラスを示します。
リスト 6. Strategy.php
<?php インターフェイス I戦略 { 関数フィルター( $record ); } クラス FindAfterStrategy は IStrategy を実装します { プライベート $_name; パブリック関数 __construct( $name ) { $this->_name = $name; } パブリック関数フィルター($record) { return strcmp( $this->_name, $record ) <= 0; } } クラス RandomStrategy は IStrategy を実装します { パブリック関数フィルター($record) { 戻り rand( 0, 1 ) >= 0.5; } } クラスUserList { プライベート $_list = array(); パブリック関数 __construct( $names ) { if ( $names != null ) { foreach( $names として $name ) { $this->_list []= $name; } } } パブリック関数 add( $name ) { $this->_list []= $name; } パブリック関数 find( $filter ) { $recs = 配列(); foreach( $this->_list as $user ) { if ( $filter->filter( $user ) ) $recs []= $user; } $recs を返します。 } } $ul = new UserList( array( "アンディ", "ジャック", "ローリ", "ミーガン" ) ); $f1 = $ul->find( new FindAfterStrategy( "J" ) ); print_r( $f1 ); $f2 = $ul->find( new RandomStrategy() ); print_r( $f2 ); ?> |
図 6. ユーザーの選択に使用されるユーザー リストとポリシー |
%php 戦略.php 配列 ( [0] => ジャック [1] =>ロリ [2] =>ミーガン ) 配列 ( [0] => アンディ [1] =>ミーガン ) % |