状态机(State)模式
定义:
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为
优点:
- 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
缺点:
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
- 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
应用场景
日常开发中经常会遇到各种状态的切换,例如电商系统的订单状态。如果我们将各种状态分散于各个地方,这样一来不利于管理,二来一旦出现问题很难查找原因和修复,并且往往很容易出现各式各样的bug,和控制的不严谨。 这个时候,我们就需要使用状态机模式来合理的控制订单状态的验证,处理和变更。
实现步骤
状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
①现态:是指当前所处的状态。
②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
代码实例
interface Action
{
public function handle($from, $event, $to);
}
class OrderAction implements Action
{
public function handle($from, $event, $to)
{
return "状态从{$from} 转换到 {$to}\n";
}
}
class Transition
{
private $from;
private $event;
private $to;
private $action;
public function __construct($from, $event, $to, Action $action = null)
{
$this->from = $from;
$this->event = $event;
$this->to = $to;
$this->action = $action;
}
public function getFrom()
{
return $this->from;
}
public function getEvent()
{
return $this->event;
}
public function getTo()
{
return $this->to;
}
public function getAction()
{
return $this->action;
}
}
abstract class State
{
protected $states = [];
protected $start = [];
protected $end = [];
protected $events = [];
protected $transitions = [];
protected $eventDesc = '';
protected $obj;
protected $currentState = 0;
protected function createMachine($allowState, $start, $end, $events, $transitions)
{
$this->setStates($allowState);
$this->setStart($start);
$this->setEnd($end);
$this->setEvents($events);
foreach ($transitions as $transition) {
$this->addTransition($transition);
}
}
private function addTransition(Transition $transition)
{
$this->transitions[$transition->getFrom()][$transition->getEvent()] = [
'to' => $transition->getTo(),
'action' => $transition->getAction(),
];
}
private function setStates($states)
{
$this->states = $states;
}
private function setStart($start)
{
$this->start = $start;
}
private function setEnd($end)
{
$this->end = $end;
}
private function setEvents($events)
{
$this->events = $events;
}
public function run($data)
{
$array = [
'status' => 2,
];
if (!in_array($array['status'], array_keys($this->transitions))) {
return "状态有误\n";
}
$stateAction = $this->transitions[$array['status']];
if (!array_key_exists($data['event'], $stateAction)) {
return "事件有误\n";
}
$stateEvent = $stateAction[$data['event']];
return $stateEvent['action']->handle($array['status'], $data['event'], $stateEvent['to']);
}
}
class Order extends State
{
public function __construct()
{
$allowState = [
1 => '开始',
2 => '结束',
];
$events = [
'add_order' => '添加',
'update_order' => '删除',
];
$transitions = [
new Transition(1, 'add_order', 2, new OrderAction()),
new Transition(2, 'update_order', 3, new OrderAction()),
];
$this->createMachine($allowState, 1, [2], $events, $transitions);
}
}
$res = (new Order())->run([
'event' => "update_order",
]);
print_r($res);