
State を PHP で
State は、 振る舞いに関するデザインパターンの一つで、 オブジェクトの内部状態が変化した時にその振る舞いを変更することを可能とします。
このパターンは、 状態に関連した振る舞いを個別の状態のクラスへ抽出し、 元のオブジェクトが作業を自分で行わず、 これらのクラスのインスタンスに委任することを強制します。
複雑度:
人気度:
使用例: PHP では、 State パターンは、 switch
演算子に基づく巨大で厄介な状態機械をオブジェクトに変換する時に時々使用されます。
見つけ方: オブジェクトが、 その外的に制御される状態によって振る舞いを変えるようなメソッドを持っていたら、 State パターンを識別できます。
概念的な例
この例は、 State デザインパターンの構造を説明するためのものです。 以下の質問に答えることを目的としています:
- どういうクラスからできているか?
- それぞれのクラスの役割は?
- パターンの要素同士はどう関係しているのか?
ここでパターンの構造を学んだ後だと、 これに続く、 現実世界の PHP でのユースケースが理解しやすくなります。
index.php: 概念的な例
<?php
namespace RefactoringGuru\State\Conceptual;
/**
* The Context defines the interface of interest to clients. It also maintains a
* reference to an instance of a State subclass, which represents the current
* state of the Context.
*/
class Context
{
/**
* @var State A reference to the current state of the Context.
*/
private $state;
public function __construct(State $state)
{
$this->transitionTo($state);
}
/**
* The Context allows changing the State object at runtime.
*/
public function transitionTo(State $state): void
{
echo "Context: Transition to " . get_class($state) . ".\n";
$this->state = $state;
$this->state->setContext($this);
}
/**
* The Context delegates part of its behavior to the current State object.
*/
public function request1(): void
{
$this->state->handle1();
}
public function request2(): void
{
$this->state->handle2();
}
}
/**
* The base State class declares methods that all Concrete State should
* implement and also provides a backreference to the Context object, associated
* with the State. This backreference can be used by States to transition the
* Context to another State.
*/
abstract class State
{
/**
* @var Context
*/
protected $context;
public function setContext(Context $context)
{
$this->context = $context;
}
abstract public function handle1(): void;
abstract public function handle2(): void;
}
/**
* Concrete States implement various behaviors, associated with a state of the
* Context.
*/
class ConcreteStateA extends State
{
public function handle1(): void
{
echo "ConcreteStateA handles request1.\n";
echo "ConcreteStateA wants to change the state of the context.\n";
$this->context->transitionTo(new ConcreteStateB());
}
public function handle2(): void
{
echo "ConcreteStateA handles request2.\n";
}
}
class ConcreteStateB extends State
{
public function handle1(): void
{
echo "ConcreteStateB handles request1.\n";
}
public function handle2(): void
{
echo "ConcreteStateB handles request2.\n";
echo "ConcreteStateB wants to change the state of the context.\n";
$this->context->transitionTo(new ConcreteStateA());
}
}
/**
* The client code.
*/
$context = new Context(new ConcreteStateA());
$context->request1();
$context->request2();
Output.txt: 実行結果
Context: Transition to RefactoringGuru\State\Conceptual\ConcreteStateA.
ConcreteStateA handles request1.
ConcreteStateA wants to change the state of the context.
Context: Transition to RefactoringGuru\State\Conceptual\ConcreteStateB.
ConcreteStateB handles request2.
ConcreteStateB wants to change the state of the context.
Context: Transition to RefactoringGuru\State\Conceptual\ConcreteStateA.
現実的な例
状態機械と State パターンの概念は、 開発者の間では人気がありますが、 実際の PHP アプリケーションでのきちんとした現実的使用例は、 思いつくことができません。
もしこのパターンを自分のプロジェクトで使ったことがあるという方いらっしゃいましたら、 ぜひその時の経験をシェアしてください。 フォーラム上で、 あるいは、 support@refactoring.guru までメールでお願いします。 よろしく!