PHPでObserverパターンで実装してみた


はじめに

Rx****をやってみたくなり、基本となる(?)ObserverパターンをPHPにて実装して見た。

前提条件として、

  • PHP5.xでも動く
  • ある程度実用的

とします。

Observerパターンとは

あちこちのページで書かれているので細かくは省略します。

状態変化に応じた処理を記述するときに有効らしいです。

今回のサンプル

タイトルと内容を入力とするフォームからPOSTされた時に、

  • メール送信
  • ログ出力

を行う処理を記述するのにObserverパターンで記述してみます。

(この程度でObserverパターンを使うのもって話はあるが。。。(汗)

クラス図はこんな感じ

ソース構成はこんな感じ

┣━ classes

┃  ┣━ ifSubject.php

┃  ┣━ clsFormdat.php

┃  ┣━ ifObserver.php

┃  ┣━ clsLogObserver.php

┃  ┣━ clsSndMailObserver.php

┗━ smp1.php

実装

インターフェイス

まずSubjectのインターフェイスは

ifSubject.php
<?php
/**
 * Subjectインターフェイス
 */
interface ifSubject {
  public function addObserver($obj);      // Observerインスタンス追加
  public function removeObserver($obj);   // Observerインスタンス削除
  public function notify();               // Observerへの通知
}

次にObserverインターフェイスは

ifObserver.php
<?php
/**
 * Observerインターフェイス
 */
interface ifObserver {
  public function execute($obj);   // Subjectからの通知受信
}

Subjectクラス

clsFormdat.php
<?php
require_once(dirname(__FILE__)."/ifSubject.php");

/**
 * フォームデータクラス
 */
class clsFormdat implements ifSubject {
  private $title = ""; /// タイトル
  private $memo = "";  /// 内容

  private $arrObservers = NULL;  /// Observerインスタンス配列

  public function __construct() {
    $this->arrObservers = array();
  }

  /**
   * タイトルと内容のセッター
   */
  public function __set($itm,$val) {
    if ( $itm == "title" ) $this->title = $val;
    if ( $itm == "memo" ) $this->memo = $val;
  }

  /**
   * タイトルと内容のゲッター
   */
  public function __get($itm) {
    if ( $itm == "title" ) return $this->title;
    if ( $itm == "memo" ) return $this->memo;
  }

  /**
   * Observerインスタンス追加
   * @param $obj Observerインスタンス
   */
  public function addObserver($obj) {
    $this->arrObservers[get_class($obj)] = $obj;
  }

  /**
   * Observerインスタンス削除
   * @param $obj Observerインスタンス
   */
  public function removeObserver($obj) {
    unset($this->arrObservers[get_class($obj)]);
  }

  /**
   * Observerへの通知
   */
  public function notify() {
    foreach($this->arrObservers as $obj) {
      $obj->execute($this);
    }
  }
}

Observerクラス

まず、ログ出力クラスは

clsLogObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * ログ出力クラス
 */
class clsLogObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    $buf  = "title:".$obj->title;
    $buf .= "\n".$obj->memo;
    $fp = fopen("/var/www/Log/app.log","a");
    fwrite($fp,$buf);
    fclose($fp);
  }
}

次にメール送信クラス

clsSndMailObserver.php
<?php
require_once(dirname(__FILE__)."/ifObserver.php");

/**
 * メール送信クラス
 */
class clsSndMailObserver implements ifObserver {
  public function __construct() {

  }

  /**
   * 通知受信
   * @param $obj clsFormdatのインスタンス
   */
  public function execute($obj) {
    mail("[email protected]",$obj->title,$obj->memo,"From: [email protected]");
  }
}

動かしてみる

次のコードで実際に動かしてみる。

smp1.php
<?php
require_once(dirname(__FILE__)."/classes/clsLogObserver.php");
require_once(dirname(__FILE__)."/classes/clsSndMailObserver.php");
require_once(dirname(__FILE__)."/classes/clsFormdat.php");

$obj = new clsFormdat();

$objLog = new clsLogObserver();
$obj->addObserver($objLog);

$objMail = new clsSndMailObserver();
$obj->addObserver($objMail);

$obj->title = "サンプルタイトル";
$obj->memo  = "サンプルサンプル\nサンプル";
$obj->notify();

確かに実装はシンプルになったようだ。