Stateパターン - PHPデザインパターン
- 公開日
- カテゴリ:DesignPatterns
- タグ:PHP,Behavior,DesignPatterns,Singleton,State

State パターン(ステート・パターン)は、は振る舞いに関するデザインパターン手法の一つで、1つの状態を1つのオブジェクトとして表現し、状態の変更をシンプルに構築できる処理モデルです。
State パターンの基本的なクラス図は以下になります。
実装例
今回は、ユーザのオンライン・オフラインの状態を切り替える機能を State パターンで実装し、その流れを見ていきます。
- App/State/Interfaces/StateInterface.php
-
<?php namespace App\State\Interfaces; interface StateInterface { public function nextState(); public function getStatus(); }
状態を切り替える nextState() メソッドと、状態を返す getStatus() メソッドを用意しました。
次に、State の具象クラスです。先ほど作成したインターフェイスを実装し、オンライン状態とオフライン状態の2つのクラスを定義します。
- App/State/OnlineState.php
-
<?php namespace App\State; use App\State\Interfaces\StateInterface; class OnlineState implements StateInterface { private static $instance = null; public static function getInstance() { if (empty(self::$instance)) { self::$instance = new OnlineState(); } return self::$instance; } public function nextState() { return OfflineState::getInstance(); } public function getStatus() { return '<span style="color: blue;">オンライン</span>'; } public final function __clone() { throw new \Exception('This Instance is Not Clone'); } public final function __wakeup() { throw new \Exception('This Instance is Not unserialize'); } }
- App/State/OfflineState.php
-
<?php namespace App\State; use App\State\Interfaces\StateInterface; class OfflineState implements StateInterface { private static $instance = null; public static function getInstance() { if (empty(self::$instance)) { self::$instance = new OfflineState(); } return self::$instance; } public function nextState() { return OnlineState::getInstance(); } public function getStatus() { return '<span style="color: darkgray;">オフライン</span>'; } public final function __clone() { throw new \Exception('This Instance is Not Clone'); } public final function __wakeup() { throw new \Exception('This Instance is Not unserialize'); } }
状態を表現する State クラスは Singleton パターンを用いて作成します(1つの状態を表すオブジェクトは複数不要で1つあればよい)。そして、nextState() メソッドで切り替えるべき次の状態を渡しています。
次に、Context クラスです。今回はユーザとして定義していきます。
- App/Context/User.php
-
<?php namespace App\Context; use App\State\OnlineState; class User { private $name; private $state; public function __construct($name) { $this->name = $name; $this->state = OnlineState::getInstance(); } public function getName() { return $this->name; } public function changeState() { $this->state = $this->state->nextState(); } public function getStatus() { return $this->state->getStatus(); } }
- コンストラクタでは、初期の状態インスタンスをセットしています。
- changeState() メソッドで次の状態へ変更しています。ユーザーは自身の処理ではなく、所持している state クラスの変更メソッドをただ実行しているだけです。
実装が完了したので、クライアントから利用してみます。
- index.php
-
<?php use App\Context\User; $user = new User('rito'); echo sprintf('ユーザ:%s 状態:%s<br><br>', $user->getName(), $user->getStatus()); // 状態を変更する $user->changeState(); echo sprintf('ユーザ:%s 状態:%s<br><br>', $user->getName(), $user->getStatus()); // 状態を変更する $user->changeState(); echo sprintf('ユーザ:%s 状態:%s<br><br>', $user->getName(), $user->getStatus()); // 状態を変更する $user->changeState(); echo sprintf('ユーザ:%s 状態:%s<br><br>', $user->getName(), $user->getStatus());
ユーザーのインスタンスを生成し、状態の切り替えを何度か行っています。見ての通り、現在のユーザーの状態を気にする事なく、changeState() メソッドを実行し状態の切り替えを行う事が可能であり、これが State パターンを用いる利点です。
実行結果は以下になります。
State パターンで状態切り替えを実装出来た事が確認できました。
まとめ
「State = 状態」を意味する通り、State パターンではその「もの」ではなく、その「状態」をクラスとして1つの機能を構築します。 State パターンを用いる事で、その「状態」の変化によって振る舞いを変更する事が出来るようになり、例えば状態の変更の為に条件分岐に頼らなくても良くなります。
State 具象クラスには Singleton パターンを採用し、状態の切り替えは Context クラスではなくあくまでも State 具象クラスで行うなど一定のルールもありますが、わりと使いやすいパターンなので是非試してみてください。