Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

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パターン

1 外部からインスタンスを生成させない
2 インスタンスをひとつだけ生成を許す
ということを実現するデザインパターン

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_;
    }   

}

client

外部のclientからnewをしようとするとerrorになる。
インスタンスを取得する場合はクラスメソッドのgetInstance()を利用する。

<?php

//error
$instance = new singleton(); //ここはエラーになる。

//success
$instance = singleton::getInstance();

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";
}