php设计模式(二十):状态模式(State)

2023-05-23 14:56:27 浏览数 (2)

状态模式

状态模式又称为:State。状态是一种行为设计模式,能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。

问题

有一个 文档 Document 类。 文档可能会处于 草稿(Draft) 、 审阅中 (Moderation)和 已发布(Published)三种状态中的一种。

文档的 发布(publish)方法在不同状态下的行为略有不同:

  1. 处于 草稿 状态时,它会将文档转移到审阅中状态。
  2. 处于 审阅中 状态时,如果当前用户是管理员,它会公开发布文档。
  3. 处于 已发布 状态时,可以撤回成 草稿 状态。

平常可能会直接将状态转换的操作写到一个方法中

代码语言:javascript复制
switch ($status) {
    case 'Draft':
        break;
    case 'Moderation':
        break;
    case 'Published':
        break;
}

为了能根据当前状态选择完成相应行为的方法,绝大部分方法中会包含复杂的条件语句。修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句,导致代码的维护工作非常艰难。这个问题会随着项目进行变得越发严重。我们很难在设计阶段预测到所有可能的状态和转换。随着时间推移,最初仅包 含有限条件语句的简洁状态机可能会变成臃肿的一团乱麻。

解决方法

我们将工作委派给一个状态对象。将所有可能状态新建一个类,然后将所有状态的对应行为抽取到这些类中。也就是说我们把具体的转化过程交个具体的状态类,而无需到使用状态的类中编写。

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

结构

State:状态基类; Draft : 实现 State 接口的具体状态类; Document:具体的使用状态模式类 ;

代码示例

状态基类

代码语言:javascript复制
abstract class State
{
    /**
     * 当前类
     * @var Document
     */
    public Document $document;

    public function __construct(Document $document)
    {
        $this->document = $document;
    }

    /**
     * 渲染
     * @return mixed
     * @author chendashengpc
     */
    abstract public function render();

    /**
     * 发布
     * @return mixed
     * @author chendashengpc
     */
    abstract public function publish();
}

具体状态类

草稿

代码语言:javascript复制
/**
 * 草稿
 */
class Draft extends State
{
    public function render()
    {
        return '草稿状态';
    }

    /**
     * 发布为审核中
     * @return mixed|void
     * @author chendashengpc
     */
    public function publish()
    {
        $this->document->changeState(new Moderation($this->document));
    }
}

审查中

代码语言:javascript复制
/**
 * 审查中
 */
class Moderation extends State
{
    public function render()
    {
        return '审查状态';
    }

    /**
     * 发布为审核中
     * @return mixed|void
     * @author chendashengpc
     */
    public function publish()
    {
        $this->document->changeState(new Published($this->document));
    }
}

已发表

代码语言:javascript复制
/**
 * 已发表
 */
class Published extends State
{
    public function render()
    {
        return '已发表状态';
    }

    /**
     * 发布成功了,再次可变为草稿
     * @return mixed|void
     * @author chendashengpc
     */
    public function publish()
    {
        $this->document->changeState(new Draft($this->document));
    }
}

用状态模式类

代码语言:javascript复制
class Document
{
    protected State $state;

    /**
     * 初始化默认状态
     */
    public function __construct()
    {
        $this->state = new Draft($this);
    }

    /**
     * 渲染
     * @return void
     * @author chendashengpc
     */
    public function render()
    {
        return $this->state->render();
    }

    /**
     * 发布
     * @return void
     * @author chendashengpc
     */
    public function publish()
    {
        return $this->state->publish();
    }

    /**
     * 修改状态
     * @param State $state
     * @return void
     * @author chendashengpc
     */
    public function changeState(State $state)
    {
        $this->state = $state;
    }
}

客户端使用

代码语言:javascript复制
$document = new Document();
// 处于草稿阶段
echo $document->render() . PHP_EOL;
// 发布为审核状态
$document->publish();
echo $document->render() . PHP_EOL;
// 发布
$document->publish();
echo $document->render() . PHP_EOL;

输出

代码语言:javascript复制
草稿状态
审查状态
已发表状态

UML

优缺点

优点

  • 单一职责原则。将与特定状态相关的代码放在单独的类中。
  • 开闭原则。无需修改已有状态类和上下文就能引入新状态。
  • 通过消除臃肿的状态机条件语句简化上下文代码。

缺点

  • 如果状态机只有很少的几个状态,或者很少发生改变,那么应用该模式可能会显得小题大作。

0 人点赞