極簡設計模式-狀態模式

long2ge發表於2021-12-10

定義

允許一個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類。

設計的原則和思想

  1. 解耦的是物件和狀態。
  2. 不變部分是物件,變化部分是狀態。
  3. 核心思想是不同狀態下做的事情也不同。

一句話概括設計模式

物件的狀態改變,行為也要隨之改變。

結構中包含的角色

  1. Context(環境類)
  2. State(抽象狀態類)
  3. 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();

優點

  1. 狀態物件可以共享,從而減少系統物件的個數。
  2. 可以簡化條件語句上下文的程式碼。
  3. 可以對狀態轉換程式碼進行集中管理。

缺點

  1. 系統中類和物件的個數會增加,導致系統執行開銷增大。
  2. 狀態模式的結構與實現都較為複雜,增加系統設計的難度。
  3. 增加新的狀態類需要修改負責狀態轉換的相關程式碼。
  4. 如果狀態太少,反而顯得臃腫。

何時使用

  1. 物件的行為依賴於它的狀態,狀態的改變將導致行為的變化。
  2. 在程式碼中包含大量與物件狀態有關的條件語句。
  3. 當不同狀態和基於條件的狀態轉換中存在許多重複程式碼時。

實際應用場景

  1. 訂單狀態切換。
  2. 遊戲角色升級。
  3. 工作流。
  4. QQ現在的狀態,線上,隱身,忙碌。

有限狀態機

    有限狀態機,也稱為FSM(Finite State Machine),其在任意時刻都處於有限狀態集合中的某一狀態。
    FSM是一種演算法思想,簡單而言,有限狀態機由一組狀態、一個初始狀態、輸入和根據輸入及現有狀態轉換為下一個狀態的轉換函式組成。
    狀態機可歸納為4個要素,即現態、條件、動作、次態。詳解如下。
    現態 : 是指當前所處的狀態。
    條件 : 又稱為“事件”。當一個條件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。
    動作 : 條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。
    次態 : 條件滿足後要遷往的新狀態。

結構中包含的角色

  1. State(狀態類)
  2. Event(事件/條件類)
  3. AbstractTransition (抽象動作類)
  4. ConcreteTransition (具體動作類)
  5. AbstractStateMachine (抽象狀態機)
  6. 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();
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Long2Ge

相關文章