【中級者向け】ゲーム設計で学ぶobserverパターン!

こんにちは!
今日はプログラムの設計でよく見るobserverパターンについてだよ!

「こういう処理が走ったら、特定のオブジェクトにこういう処理をさせたい!」って時よくあるよね???ゲーム設計に限らずだと思うけど!

その時にif文case文地獄にならないように設計できるのがobserverパターン!

用意するのはざっくり2つ!

1、「こういう処理が走ったら」を感知するsubject

2、その感知に対してどういう処理を行うかのObserver

subjectがObserverを管理して登録したり削除したり感知するメソッドを用意!
Observerは感知された時に呼び出される処理を書く!

今回は花壇に水をやってそれを感知する花と花壇の水量を観察するクラスを作っていきましょう!
今回はC#で書くかphpで書くか迷ったけど最近phpばっかだからphp(言語は違っても考え方は同じだから!!!)

まずは花壇クラスを作成!上で言うsubjectの部分!
Flowerbed.php

<?php 
/**
 * Subjectクラス+ConcreteSubjectクラスに相当する
 */ 
class Flowerbed { 
  private $waterCnt; 
  private $listeners; 
  public function __construct() { 
    $this->listeners = array();
    $waterCnt = 0;
  }

  /**
  * 植物に水を与えたときに呼ぶメソッド
  */
  public function addWater()
  {
    $this->$waterCnt++;
    $this->notify();
  }

  /**
  * 水を与えた回数を取得
  */
  public function getWaterCnt()
  {
    return $this->$waterCnt;
  }

  /**
  * Observerを登録するメソッド
  */
  public function addListener(PlantListener $listener)
  {
    $this->listeners[get_class($listener)] = $listener;
  }

  /**
  * Observerを削除するメソッド
  */
  public function removeListener(PlantListener $listener)
  {
    unset($this->listeners[get_class($listener)]);
  }

  /**
  * Observerへ通知するメソッド
  */
  public function notify()
  {
    foreach ($this->listeners as $listener) {
      $listener->update($this);
    }
  }
}
?>

次はインターフェース部分!抽象メソッドを定義するよ!
PlantListener.php

<?php
interface PlantListener {
    public function update(Flowerbed $flowerbed);
}
?>

次は花壇の水量を観察するクラスを作っていこう!(上で言うObserverのとこ!)
AmountWater.class.php

<?php
  require_once 'PlantListener.php';
/** 
* Observerクラスに相当する 
*/
class AmountWater implements PlantListener { 
  public function __construct() { } 
  public function update(Flowerbed $flowerbed) {
   echo '現在の水量は'.$flowerbed->getWaterCnt().'です';
 }
}
?>

次は具体的な花のクラスを作るよ!今回はアサガオと向日葵!
Asagao.class.php

<?php
require_once 'PlantListener.php';

/**
 * Observerクラスに相当する
 */
class Asagao implements PlantListener
{
    public function __construct(){}
    public function update(Flowerbed $flowerbed)
    {
     echo 'アサガオが水をもらいました';
    }
}
?>

Himawari.class.php

<?php
require_once 'PlantListener.php';

/**
 * Observerクラスに相当する
 */
class Himawari implements PlantListener
{
    public function __construct(){}
    public function update(Flowerbed $flowerbed)
    {
      echo '向日葵が水をもらいました';
    }
}
?>

これで準備おK! あとは水をあげるボタンを用意してそれぞれのオブジェクトをよぼう!
observer_client.php

<?php
 require_once 'Flowerbed.php';
 require_once 'Himawari.class.php';
 require_once 'Asagao.class.php';
 require_once 'AmountWater.class.php'; 
//花壇と花を作成してSubjectに登録 
function createCart() { 
 $flowerbed = new Flowerbed(); 
 $flowerbed->addListener(new Himawari());
 $flowerbed->addListener(new Asagao());
 $flowerbed->addListener(new AmountWater());
 return $flowerbed;
}

session_start();
$flowerbed = isset($_SESSION['flowerbed']) ? $_SESSION['flowerbed'] : null;

if (is_null($flowerbed)) {
 $flowerbed = createCart();
 $_SESSION['flowerbed'] = $flowerbed;
}

$mode = (isset($_POST['mode']) ? $_POST['mode'] : '');

//水をあげた時にFlowerbedのaddWater関数を呼ぶ
if ($mode != '') {
 $flowerbed->addWater();
}

?>
<form method="post" action="">
 <input type="hidden" name="mode" value="add">
 <input type="submit" value="水をあげる">
</form>

これでobserver_client.phpを開いて水をあげれば下記のようになるよ!

今回は文字を表示させているだけだけど、実際に走らせたい処理を書いてあげるといいよ!
花が増えたり、観測者を増やしたいなら別途そのオブジェクトを管理するファイルを作って登録させるだけ!
これで条件分岐地獄にはならずにオブジェクトごとに管理もしやすくてバグも起こりにくいよ!

ちょっと長かったけどぜひこの設計やって見てね!
以上!河条でした^o^