狀態模式從字面上其實并不是很好理解,這里的狀態是什么意思呢?保存狀態?那不就是備忘錄模式了,其實,這里的狀態是類的狀態,通過改變類的某個狀態,讓這個類感覺像是換了一個類一樣,說起來有點拗口吧,先學習概念之后再看,
Gof類圖及解釋
GoF定義:允許一個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類
GoF類圖

代碼實作
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
實體
通常的商城應用中都會有會員體系的存在,一般等級越高的會員可以享受的折扣也會越多,這個時候,運用狀態模式就能很輕松的獲得會員的等級折扣,當然,最主要的是,使用狀態模式可以在需要添加或者洗掉會員等級時只添加對應的會員折扣狀態子類就可以了,其他業務代碼都不需要變動,我們一起來看看具體實作吧!
會員折扣圖

完整原始碼: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 '當前會員' . $m->GetScore() . '積分,折扣為:' . $m->discount(), PHP_EOL;
$m->SetScore(990);
echo '當前會員' . $m->GetScore() . '積分,折扣為:' . $m->discount(), PHP_EOL;
$m->SetScore(660);
echo '當前會員' . $m->GetScore() . '積分,折扣為:' . $m->discount(), PHP_EOL;
$m->SetScore(10);
echo '當前會員' . $m->GetScore() . '積分,折扣為:' . $m->discount(), PHP_EOL;
說明
- 如果不使用狀態模式,在Member的discount()方法中,我們可能需要寫很多層if...else...判斷條件
- 同時,這也帶來了方法體會越來越長,越來越難以維護的問題
- 狀態模式正是為了解決這個問題而存在的
- 當discount()行為的結果依賴于Member物件本身的狀態(會員分)時,狀態模式就是最佳的選擇了,也就是上面所說的一個物件的行為取決于它的狀態
下期看點
狀態模式其實運用的范圍很廣,但使用的人確不多,畢竟if...else...更加的直觀,而且大部分日常應用中的狀態一般也很少會去修改或添加,如果你的訂單狀態需要經常的修改或添加,那肯定在架構設計中存在著問題,但是,通過這個模式的學習,我們看到了面向物件在處理這種問題時所展現的威力,這才是我們對設計模式學習的最終目的,靈活合適地運用,更深入的了解面向物件,好了,最后一個設計模式就要登場了,它就是訪問者模式,
關注公眾號:【硬核專案經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜索【硬核專案經理】
B站ID:482780532
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/245956.html
標籤:PHP
上一篇:Java高并發17-LongAccumulator類詳解
下一篇:有無符號強轉換問題
