首頁 >後端開發 >php教程 >PHP中什麼是狀態模式?透過實例來了解它

PHP中什麼是狀態模式?透過實例來了解它

青灯夜游
青灯夜游轉載
2021-07-27 18:33:362164瀏覽

在之前的文章《一起聊聊PHP中的單例模式》中我們介紹了PHP中的單例模式,以下這篇文章帶大家了解一下PHP設計模式中的狀態模式

PHP中什麼是狀態模式?透過實例來了解它

狀態模式從字面上其實不是很好理解。這裡的狀態是什麼意思呢?保存狀態?那不就是備忘錄模式了。其實,這裡的狀態是類別的狀態,透過改變類別的某個狀態,讓這個類別感覺像是換了一個類別一樣。說起來有點拗口吧,先學習概念之後再看。

Gof類別圖及解釋

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

GoF類別圖

PHP中什麼是狀態模式?透過實例來了解它

#程式碼實現

class Context
{
    private $state;
    public function SetState(State $state): void
    {
        $this->state = $state;
    }
    public function Request(): void
    {
        $this->state = $this->state->Handle();
    }
}

一個上下文類,也可以看作是目標類,它的內部有一個狀態物件。當呼叫Request()的時候,去呼叫狀態類別的Handle()方法。目的是當前上下文類別狀態的變化都由外部的這個狀態類別來進行操縱。

interface State
{
    public function Handle(): State;
}

class ConcreteStateA implements State
{
    public function Handle(): State
    {
        echo '当前是A状态', PHP_EOL;
        return new ConcreteStateB();
    }
}

class ConcreteStateB implements State
{
    public function Handle(): State
    {
        echo '当前是B状态', PHP_EOL;
        return new ConcreteStateA();
    }
}

抽象狀態介面及兩個具體實作。這兩個具體實作其實是在相互呼叫。實現的效果就是上下文類別每呼叫一次Request()方法,內部的狀態類別就變成別一個狀態。就像一個開關,在開啟與關閉中來回切換一樣。

$c = new Context();
$stateA = new ConcreteStateA();
$c->SetState($stateA);
$c->Request();
$c->Request();
$c->Request();
$c->Request();

客戶端的實現,實例化上下文物件並設定初始的狀態,然後透過不停的呼叫Request()物件來實現開關狀態的切換。

  • 看出門道了嘛?這裡把狀態的變化給封裝到外部的實作類別去了,並不是這個上下文或目標類別內部來進行狀態的切換了
  • 那麼狀態模式的意義呢?這個預設類別圖的例子過於簡單,其實狀態模式的真正目的是為了解決複雜的if嵌套問題的,把複雜的if嵌套條件放到一個個的外部狀態類別中去判斷,在後面的實例中我們會看到
  • 適用於:一個物件的行為取決於它的狀態,並且它的必須在運行時刻根據狀態改變自己的行為;一個操作中含有大量的多分支條件語句,而這些分支依賴於該物件的狀態;
  • 狀態模式的特徵是:它將與特定狀態相關的行為局部化;它使得狀態轉換明確化;State物件可以被共享;
  • 常見於訂單系統、會員系統、OA系統中,也就是流程中會出現各種狀態變化的情況,都可以使用狀態模式來進行整體的設計與架構

#我們的手機系統內客製了自己的商城系統,可以在手機上方便的下單購買我們的商品。一個訂單(Context)會有多種狀態(State),例如未支付、已支付、訂單完成、訂單退款等等一大堆狀態。我們把這些狀態都放在了對應的狀態類別裡去實現,不同的狀態類別都會再去調用該狀態下一步的動作,例如已支付後就等待收貨、退款後就等待買家填寫物流單號等,這樣,狀態模式就在我們的商城中被靈活的運用起來咯! !

完整程式碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/22.state/source/state.php

#實例

通常的商城應用程式中都會有會員體系的存在,一般等級越高的會員可以享受的折扣也會越多,這個時候,運用狀態模式就能很輕鬆的獲得會員的等級折扣。當然,最主要的是,使用狀態模式可以在需要新增或刪除會員等級時只新增對應的會員折扣狀態子類別就可以了。其他業務程式碼都不需要變動,我們一起來看看具體實作吧!

會員折扣圖

PHP中什麼是狀態模式?透過實例來了解它

#完整原始碼:https://github.com/zhangyue0503/designpatterns-php/ blob/master/22.state/source/state-member.php

<?php

class Member
{
    private $state;
    private $score;

    public function SetState($state)
    {
        $this->state = $state;
    }

    public function SetScore($score)
    {
        $this->score = $score;
    }

    public function GetScore()
    {
        return $this->score;
    }

    public function discount()
    {
        return $this->state->discount($this);
    }
}

interface State
{
    public function discount($member);
}

class PlatinumMemeberState implements State
{
    public function discount($member)
    {
        if ($member->GetScore() >= 1000) {
            return 0.80;
        } else {
            $member->SetState(new GoldMemberState());
            return $member->discount();
        }
    }
}

class GoldMemberState implements State
{
    public function discount($member)
    {
        if ($member->GetScore() >= 800) {
            return 0.85;
        } else {
            $member->SetState(new SilverMemberState());
            return $member->discount();
        }
    }
}

class SilverMemberState implements State
{
    public function discount($member)
    {
        if ($member->GetScore() >= 500) {
            return 0.90;
        } else {
            $member->SetState(new GeneralMemberState());
            return $member->discount();
        }
    }
}

class GeneralMemberState implements State
{
    public function discount($member)
    {
        return 0.95;
    }
}

$m = new Member();
$m->SetState(new PlatinumMemeberState());

$m->SetScore(1200);
echo &#39;当前会员&#39; . $m->GetScore() . &#39;积分,折扣为:&#39; . $m->discount(), PHP_EOL;

$m->SetScore(990);
echo &#39;当前会员&#39; . $m->GetScore() . &#39;积分,折扣为:&#39; . $m->discount(), PHP_EOL;

$m->SetScore(660);
echo &#39;当前会员&#39; . $m->GetScore() . &#39;积分,折扣为:&#39; . $m->discount(), PHP_EOL;

$m->SetScore(10);
echo &#39;当前会员&#39; . $m->GetScore() . &#39;积分,折扣为:&#39; . $m->discount(), PHP_EOL;

#說明

  • 如果不使用狀態模式,在Member的在discount()方法中,我們可能需要寫很多層if...else...判斷條件
  • 同時,這也帶來了方法體會越來越長,越來越難以維護的問題
  • 狀態模式正是為了解決這個問題而存在的
  • 當discount()行為的結果依賴於Member物件本身的狀態(會員分)時,狀態模式就是最佳的選擇了,也就是上面所說的一個物件的行為取決於它的狀態

原文網址:https://juejin.cn/post/6844903991562731534

作者:硬核專案經理

推薦學習:《PHP影片教學

以上是PHP中什麼是狀態模式?透過實例來了解它的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除