状态模式 - State Pattern
定义
允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
设计的原则和思想
- 解耦的是对象和状态。
- 不变部分是对象,变化部分是状态。
- 核心思想是不同状态下做的事情也不同。
一句话概括设计模式
对象的状态改变,行为也要随之改变。
结构中包含的角色
- Context(环境类)
- State(抽象状态类)
- ConcreteState(具体状态类)
最小可表达代码
abstract class State
{
public abstract function handle();
}
class ConcreteState extends State
{
public function handle()
{
echo '具体状态';
}
}
class Context
{
private $state;
public function setState(State $state)
{
$this->state = $state;
}
public function request()
{
$this->state->handle();
}
}
$context = new Context();
$context->setState(new ConcreteState());
$context->request();
优点
- 状态对象可以共享,从而减少系统对象的个数。
- 可以简化条件语句上下文的代码。
- 可以对状态转换代码进行集中管理。
缺点
- 系统中类和对象的个数会增加,导致系统运行开销增大。
- 状态模式的结构与实现都较为复杂,增加系统设计的难度。
- 增加新的状态类需要修改负责状态转换的相关代码。
- 如果状态太少,反而显得臃肿。
何时使用
- 对象的行为依赖于它的状态,状态的改变将导致行为的变化。
- 在代码中包含大量与对象状态有关的条件语句。
- 当不同状态和基于条件的状态转换中存在许多重复代码时。
实际应用场景
- 订单状态切换。
- 游戏角色升级。
- 工作流。
- QQ现在的状态,在线,隐身,忙碌。
有限状态机
有限状态机,也称为FSM(Finite State Machine),其在任意时刻都处于有限状态集合中的某一状态。
FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。
状态机可归纳为4个要素,即现态、条件、动作、次态。详解如下。
现态 : 是指当前所处的状态。
条件 : 又称为“事件”。当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
动作 : 条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
次态 : 条件满足后要迁往的新状态。
结构中包含的角色
- State(状态类)
- Event(事件/条件类)
- AbstractTransition (抽象动作类)
- ConcreteTransition (具体动作类)
- AbstractStateMachine (抽象状态机)
- ConcreteStateMachine (具体状态机)
有限状态机管理状态的代码
// 状态
class State
{
private $stateCode;
public function __construct(String $stateCode)
{
$this->stateCode = $stateCode;
}
public function getStateCode()
{
return $this->stateCode;
}
}
// 事件
class Event
{
private $eventCode;
private $parameters = [];
public function __construct(String $eventCode, array $parameters = [])
{
$this->eventCode = $eventCode;
$this->parameters = $parameters;
}
public function getEventCode()
{
return $this->eventCode;
}
public function getParameters() : array{
return $this->parameters;
}
}
// 抽象动作
abstract class AbstractTransition
{
protected $currentState; // 现态
protected $nextState; // 次态
public function __construct(State $currentState, State $nextState)
{
$this->currentState = $currentState;
$this->nextState = $nextState;
}
// 具体动作需要执行的方法
protected abstract function subHandle(Event $event) : bool;
// 定义动作执行的方法
public function handle(Event $event)
{
if ($this->subHandle($event)) {
return $this->nextState;
}
echo 'throw new Exception 抛错';
}
}
// 抽象状态机
abstract class AbstractStateMachine
{
protected abstract function getStateEventTransitionMap() : array;
protected function getTransition(String $stateCode, String $eventCode)
{
$stateEventTransitionMap = $this->getStateEventTransitionMap();
$eventTransitionMap = $stateEventTransitionMap[$stateCode] ?? [];
$transition = $eventTransitionMap[$eventCode] ?? [];
return $transition;
}
public function handle(String $stateCode, Event $event)
{
$eventCode = $event->getEventCode();
if ($transition = $this->getTransition($stateCode, $eventCode)) {
return $transition->handle($event);
}
echo 'throw new Exception 抛错';
}
}
// 订单事件编码
class OrderEventCode {
const PAY = "支付订单";
const CANCEL = "取消订单";
}
// 订单状态编码
class OrderStateCode {
const UNPAID = "待支付";
const PAID = "已支付";
const CANCELED = "已取消";
}
// 支付动作
class PayTransition extends AbstractTransition
{
protected function subHandle(Event $event) : bool{
var_dump('支付订单', $event->getParameters());
return true;
}
}
// 订单状态机
class OrderStateMachine extends AbstractStateMachine
{
public function getStateEventTransitionMap() : array{
return [
OrderStateCode::UNPAID => [
OrderEventCode::PAY => new PayTransition(
new State(OrderStateCode::UNPAID), new State(OrderStateCode::PAID)
),
],
];
}
public function pay()
{
$event = new Event(OrderEventCode::PAY, ['order_id' => 1]);
$this->handle(OrderStateCode::UNPAID, $event);
}
}
// 支付
$stateMachine = new OrderStateMachine();
$stateMachine->pay();