Observerパターン
概要
このパターンは監視するクラス/監視されるクラスから成り立ち、監視されるクラスで特定の処理が入ったときに
監視クラスにメッセージを送信することができるというもの。例えば監視されるクラスで処理が終わったときに
監視クラスに通知を行ない、監視側で処理を動かしたい時になど利用出来る。ここで重要なことは監視されるクラスは
監視クラスの状態を知る必要はなく、自身の状態を監視クラスに伝えることのみを行えばよいためクラス関係は疎結合
になる。
監視クラス
<?php //監視を行うクラスinterface interface Observer { public static function update( $subject, $args ); } //監視クラスの実装 class ObserverClass implements Observer { public static function update( $subject, $args ) { echo( "$subject is update . args = $args" ); } }
監視するクラスは監視されるクラス側から呼び出される通知用のメソッドのみを用意してあげます。
監視されるクラス
<?php //監視をされるクラスinterface interface Subject { public static function addObserver( Observer $observer ); } //監視をされるクラスの実装 class TestSubject implements Subject { private static $_observers = array(); //メソッドが呼び出される度に監視クラスに通知 public static function addCustomer( $name ) { foreach( self::$_observers as $obs ) { $obs::update( __CLASS__, $name ); } } //監視クラスの追加 public static function addObserver( Observer $observer ) { self::$_observers[] = $observer; } }
監視されるクラスは監視するObserverクラスを登録するようにします。登録している監視クラス全てに対して
通知を行うようにします。ここでは監視クラスに用意したupdateメソッドを呼び出します。
client
<?php $ts = new TestSubject(); $ts::addObserver( new ObserverClass() ); $ts::addCustomer( "TEST CODE" );
監視されるクラスをインスタンス化し、監視クラスを追加しているだけです。
出力結果
TestSubject is update . args = TEST CODE
処理結果は上のようになります。
PHPでFilterChainを実装してみた
説明
前処理と後処理を各FilterClassに実装してFilterをChainさせます。
処理の順番としては
■前処理(prefilter)
filterA → filterB → filterC
■後処理(postfilter)
filterC → filterB → filterA
というように後処理は前処理と逆に行われます。
FilterChainクラス
<?php class FilterManager { private static $_filters = array(); private static $_index = 0; private function __construct(){} public function build( array $filters ) { foreach( $filters as $filter ) { $filter_name = ucfirst( $filter ) . 'Filter'; $file_name = $class_name . '.php'; //require_once( './Filter.php' ); require_once( $file_name ); self::add( $filter_name ); } } static private function add( $filter ) { self::$_filters[] = $filter; } static public function execute() { // prefilter foreach( self::$_filters as $filter ) { $filter::prefilter(); ++self::$_index; } // postfilter foreach( array_reverse( self::$_filters ) as $filter ) { $filter::postfilter(); --self::$_index; } } static public function getCurrentFilter() { return self::$_filters[ self::$_index ]; } }
FIlterChainを構築するbuildメソッド
FilterChainにFilterを追加するaddメソッド
FilterChainを実行するexecuteメソッド
現在実行中のFilterを取得するgetCurrentFilterメソッド
を定義します。
Filterのinterface
<?php interface Filter { static public function prefilter(); static public function postfilter(); }
prefilter,postfilterのメソッドを定義します。
各Filterの定義
<?php class ViewFilter implements Filter { static public function prefilter() { echo "View Prefilter \n"; } static public function postfilter() { echo "View Postfilter \n"; } } class ActionFilter implements Filter { static public function prefilter() { echo "Action Prefilter \n"; } static public function postfilter() { echo "Action Postfilter \n"; } }
本来はprefilter,postfilterにそれぞれ的確な内容を記述すべきですが、
今はサンプルなので適当にechoだけ書いておきます。
client
<?php /* client */ require_once( './FilterManager.php' ); //呼び出しfilterの決定 $filters = array( 'view', 'action' ); //filterのbuildと実行 FilterManager::build( $filters ); FilterManager::execute();
FilterChainを実行してくれるFilterManagerを呼び出し、配列形式で利用したいFilterクラス名を渡します。
実行結果
View Prefilter Action Prefilter Action Postfilter View Postfilter
prefilterとpostfilterの実行の順番が逆になっていることが分かります。
magic method
phpのマジックメソッドについて挙動を確認してみました。
とりあえずは代表的なもののみをピックアップ。
<?php class MagicMethod { private $data_ = array(); public function __construct() { echo "call construct \n"; } public function __destruct() { echo "call destruct \n"; } public function __get( $key ) { echo "call __get \n"; return $this->data_[ $key ]; } public function __set( $key, $name ) { $this->data_[ $key ] = $name; echo "call __set \n"; } public function __toString() { return __CLASS__; } public function __call( $name, $arguments ) { $arg = implode( "," , $arguments ); echo "call func class = $name arg = $arg \n"; } } //__construct $magic = new MagicMethod(); //__call $magic->magic( 'test', array( 'arg1', 'arg2' ) ); //__set $magic->key = 'test'; // __get echo $magic->key . "\n"; //__toString echo $magic . "\n"; //__destruct unset( $magic ); echo "process is finished \n";
以下は出力結果です。
call construct call func class = magic arg = test,Array call __set call __get test MagicMethod call destruct process is finished
abstract static宣言
abstract staticの宣言がphpで出来ます。継承した子クラスでもstaticで呼び出すことが出来ます。
<?php abstract class hoge { abstract static function foo (); } class fuga extends hoge { static public function foo() { echo "Foo \n"; } } fuga::foo();
API Frameworkの設計
やりたいこと
APIのように同一の処理の流れを組み込む場合、TemplateMethodパターンを利用してFrameWork化することができると思います。
PHPのFrameWorkはたくさん世の中に出回っていると思いますが、必要用途のモノだけを揃えた軽量FrameWorkが存在しないので
自作します。
1.APIを作成する人はModelクラスだけに手を入れる。Controllerも修正可能にしますが、極力修正させたくない。
2.Validate,DB接続,View機能はFrameWorkとして揃える。
処理の流れ
1. ControllerはApplicationからリクエストされたURIを取得します。
2. ApplicationからリクエストされたURIを元に設定ファイルを読み込みます。( 設定ファイルはphpのiniファイル形式とする予定 )
3. ApplicationからリクエストされたURIを元に必要なModelクラスを読み込みます。
4. Modelクラス内部でApplicationからリクエストされたパラメータを取得します。(API作者が実装)
5. Modelクラスは取得したパラメータのvalidateを行ないます。(API作者が実装)
6.DBに接続が必要な場合はDBクラスとConnectionを張って、データを取得します。(API作者が実装)
7.ModelクラスはDBから取得したデータの整形を行ないます。(API作者が実装)
8.ControllerにModelの結果を返却します。
9.ControllerにてView用のデータ整形を行ないます。
10.ApplicationにAPIとしての結果を返却します。
実装
各パートの実装はちょくちょくやっています。
本気でやったら数時間で終わりそうですね。
Singleton
Singletonクラス
<?php class singleton { private static $instance_ = null; private function __construct() { echo "make instance \n"; } public static function getInstance() { if( is_null( self::$instance_ ) ) { self::$instance_ = new singleton(); } return self::$instance_; } }
PHPのvalidate処理実装
validate処理
- frameworkには必ずといっていいほど含まれているvalidate処理。独自に簡単な処理を書いてみた。
- validateは処理の型が決まっているので、interfaceを用意し、個別のvalidateクラスを実装する。
interface
validateのinterfaceを定義
<?php interface Validate { public function execute( $param, $format ); }
各validate処理
interfaceで定義したexecuteメソッドの本実装を行う。ここではStirng,Length,Regexを定義するが同様にクラスを追加すれば簡単にvalidateが実装できる。
<?php class StringValidate implements Validate { public function execute( $param, $flag ) { if( is_string( $param ) !== $flag ) { return false; } return true; } } class LengthValidate implements Validate { public function execute( $param , $length ) { if( mb_strlen( $param ) > $length ) { return false; } return true; } } class RegexValidate implements Validate { public function execute( $param , $regex ) { if( !preg_match( $regex, $param ) ) { return false; } return true; } }
validateManager
clientから呼び出され、各validateのexecuteメソッドを呼び出す。
<?php class ValidateManager { static private $params_ = array(); static public function init() { //本来はここで$_POSTなどから取得する self::$params_ = array( 'aaaa' => 'sampletest' ); } static public function execute( $form ) { self::init(); foreach( $form as $key => $valide ) { foreach( $valide as $type => $format ) { $class = ucfirst( $type ) . 'Validate'; $value = self::$params_[ $key ]; $result = $class::execute( $value, $format ); if( $result === false ) { echo "ERROR $type \n"; return false; } } } return true; } }
clientプログラム
必要なvalidateの定義を行ない、validateManagerクラスに渡す。validateの定義を配列で行っているが設定ファイルから呼び出す方針に変更も可能。
<?php //client $form = array( 'aaaa' => array( 'String' => TRUE, 'Length' => 15, 'Regex' => '/\w.*/i' ) ); // 実行 $stat = ValidateManager::execute( $form ); if( $stat ) { echo "TRUE \n"; } else { echo "FALSE \n"; }