1. Home
  2. PHP
  3. DesignPatterns
  4. Stateパターン - PHPデザインパターン

Stateパターン - PHPデザインパターン

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

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級