Stateパターン - PHPデザインパターン
- 公開:
- カテゴリ: 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具象クラスで行うなど一定のルールもありますが、わりと使いやすいパターンなので是非試してみてください。