PHP设计模式之状态模式

有限状态机

状态模式一般用于实现状态机,有限状态机是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型,被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。

状态机有三个组成部分:状态、事件、动作。事件触发状态的转移及动作的执行。但动作不是必须的,有可能只有状态的转移而没有对应的动作。

举个例子,一般商家用户注册完的时候,默认状态为待审核,管理员在后台看到了商家的审核信息后,会有审核通过和审核不通过两个按钮,当选择审核不通过按钮后,商家可以继续修改信息然后提交,这个时候又会变为待审核状态。

这里面,有三种状态,待审核、通过、不通过状态,他们之间是可以相互转换的。

如果,我们用平时的思维,那么写出的代码类似如下:

class State
{
   const STATE_AUTH = 0;
   const STATE_OK  = 1;
   const STATE_FAIL = -1;
   private $state = 0;

   // 审核通过
   public function pass ()
  {
       if ($this->state == 0) {
           $this->state = 1;
           echo '审核通过' . PHP_EOL;
      } else if ($this->state == -1) {
           echo '请先修改信息,然后等待审核' . PHP_EOL;
      } else {
           // nothing
      }
  }

   public function fail ()
  {
       if ($this->state == 0) {
           $this->state = -1;
           echo '审核不通过' . PHP_EOL;
      } else if ($this->state == -1) {
           echo '请先修改信息,然后等待审核' . PHP_EOL;
      } else {
           $this->state = -1;
           echo '该商户含虚假信息,请先整改' . PHP_EOL;
      }
  }
}

目前,我们的状态还不是很多,就只有3种,所以上面的代码也没太大的问题,但是如果,我们新增几种状态,比如,初审通过(基本信息验证)、二审(身份信息验证)等,那么我们的代码将会出现大量的if else结构语句,代码的可读性以及可维护性就变的极差。

状态模式

状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。

原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象。

如需将上下文转换为另外一种状态, 则需将当前活动的状态对象替换为另外一个代表新状态的对象。 采用这种方式是有前提的: 所有状态类都必须遵循同样的接口, 而且上下文必须仅通过接口与这些对象进行交互。

现在,我们用新学的设计模式来重写上面的代码:

<?php

namespace app\state;

interface State
{
   public function pass ();

   public function fail ();
}

class Content implements State
{
   protected $context;

   public function setContent (State $content)
  {
       $this->context = $content;
  }

   public function pass ()
  {
       $this->context->pass();
  }

   public function fail ()
  {
       $this->context->fail();
  }
}

class Authed implements State
{
   public function pass()
  {
       echo '审核通过' . PHP_EOL;
  }

   public function fail ()
  {
       echo '审核不通过' . PHP_EOL;
  }
}

class Passed implements State
{
   public function pass()
  {
       // nothing
  }

   public function fail ()
  {
       echo '该商户含虚假信息,请先整改' . PHP_EOL;
  }
}

class Failed implements State
{
   public function pass()
  {
       echo '请先修改信息,然后等待审核' . PHP_EOL;
  }

   public function fail ()
  {
       echo '请先修改信息,然后等待审核' . PHP_EOL;
  }
}

测试代码如下:

$content = new Content();
$content->setContent(new Authed());
$content->pass();

与策略模式比较

这个结构可能看上去与策略模式相似, 但有一个关键性的不同——在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换; 策略则几乎完全不知道其他策略的存在。

总结

状态模式是状态机的一种实现方式即可。状态机又叫有限状态机,它有 3 个部分组成:状态、事件、动作。其中,事件也称为转移条件。事件触发状态的转移及动作的执行。不过,动作不是必须的,也可能只转移状态,不执行任何动作。

文章部分内容参考自:https://refactoringguru.cn/design-patterns/state