深入淺析PHP中的指令模式

青灯夜游
發布: 2023-04-10 10:36:02
轉載
2745 人瀏覽過

在先前的文章《一文了解PHP中的原型模式》中我們介紹了PHP中的原型模式,以下這篇文章帶大家了解一下PHP中的指令模式。

深入淺析PHP中的指令模式

指令模式,也稱為動作或交易模式,許多教材會用餐廳來舉例。作為顧客的我們是命令的下達者,服務員是這個命令的接收者,菜單是這個實際的命令,而廚師是這個命令的執行者。

那麼,這個模式解決了什麼呢?當你要修改菜單的時候,只需要和服務生說就好了,她會轉達給廚師,也就是說,我們實現了顧客和廚師的解耦。也就是呼叫者與實現者的解耦。

當然,很多設計模式可以做到這一點,但是命令模式能夠做到的是讓一個命令接收者實現多個命令(服務員下單、拿酒水、上菜),或者把一條命令轉達給多位實現者(熱菜廚師、涼菜廚師、主食師傅)。這才是命令模式真正發揮的地方! !

Gof類別圖及解釋

GoF定義:將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作

#GoF類別圖

深入淺析PHP中的指令模式

##程式碼實作

class Invoker
{
    public $command;
    
    public function __construct($command)
    {
        $this->command = $command;
    }

    public function exec()
    {
        $this->command->execute();
    }
}
登入後複製

首先我們定義一個指令的接收者,或是指令的請求者比較恰當。類圖中的英文定義這個字是「祈求者」。也就是由它來發起和操作命令。

abstract class Command
{
    protected $receiver;

    public function __construct(Receiver $receiver)
    {
        $this->receiver = $receiver;
    }

    abstract public function execute();
}

class ConcreteCommand extends Command
{
    public function execute()
    {
        $this->receiver->action();
    }
}
登入後複製

接下來是指令,也就是我們的「選單」。這個命令的作用是為了定義真正的執行者是誰。

class Receiver
{
    public $name;

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

    public function action()
    {
        echo $this->name . '命令执行了!', PHP_EOL;
    }
}
登入後複製

接管者,也就是執行者,真正去執行命令的人。

// 准备执行者
$receiverA = new Receiver('A');

// 准备命令
$command = new ConcreteCommand($receiverA);

// 请求者
$invoker = new Invoker($command);
$invoker->exec();
登入後複製

客戶端的調用,我們要聯絡好執行者也就是挑有好廚子的飯館(Receiver),然後準備好命令也就是菜單(Command),最後交給服務員(Invoker)。

    其實這個飯店的例子已經非常清晰了,對於命令模式真是完美的解析
  • 那說好的可以下多份訂單或給多個廚師呢?別急,下面的程式碼幫助我們解決這個問題
完整程式碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/ command.php

<?php

class Invoker
{
    private $command = [];

    public function setCommand(Command $command)
    {
        $this->command[] = $command;
    }

    public function exec()
    {
        if(count($this->command) > 0){
            foreach ($this->command as $command) {
                $command->execute();
            }
        }
    }

    public function undo()
    {
        if(count($this->command) > 0){
            foreach ($this->command as $command) {
                $command->undo();
            }
        }
    }
}

abstract class Command
{
    protected $receiver;
    protected $state;
    protected $name;

    public function __construct(Receiver $receiver, $name)
    {
        $this->receiver = $receiver;
        $this->name = $name;
    }

    abstract public function execute();
}

class ConcreteCommand extends Command
{
    public function execute()
    {
        if (!$this->state || $this->state == 2) {
            $this->receiver->action();
            $this->state = 1;
        } else {
            echo $this->name . &#39;命令正在执行,无法再次执行了!&#39;, PHP_EOL;
        }

    }
    
    public function undo()
    {
        if ($this->state == 1) {
            $this->receiver->undo();
            $this->state = 2;
        } else {
            echo $this->name . &#39;命令未执行,无法撤销了!&#39;, PHP_EOL;
        }
    }
}

class Receiver
{
    public $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function action()
    {
        echo $this->name . &#39;命令执行了!&#39;, PHP_EOL;
    }
    public function undo()
    {
        echo $this->name . &#39;命令撤销了!&#39;, PHP_EOL;
    }
}

// 准备执行者
$receiverA = new Receiver(&#39;A&#39;);
$receiverB = new Receiver(&#39;B&#39;);
$receiverC = new Receiver(&#39;C&#39;);

// 准备命令
$commandOne = new ConcreteCommand($receiverA, &#39;A&#39;);
$commandTwo = new ConcreteCommand($receiverA, &#39;B&#39;);
$commandThree = new ConcreteCommand($receiverA, &#39;C&#39;);

// 请求者
$invoker = new Invoker();
$invoker->setCommand($commandOne);
$invoker->setCommand($commandTwo);
$invoker->setCommand($commandThree);
$invoker->exec();
$invoker->undo();

// 新加一个单独的执行者,只执行一个命令
$invokerA = new Invoker();
$invokerA->setCommand($commandOne);
$invokerA->exec();

// 命令A已经执行了,再次执行全部的命令执行者,A命令的state判断无法生效
$invoker->exec();
登入後複製
    這次我們一次解決了多個訂單、多位廚師的問題,並且還順便解決瞭如果下錯命令了,進行撤銷的問題
  • 可以看出來,命令模式將呼叫操作的物件與知道如何實現該操作的物件實現了解耦
  • 這種多命令多執行者的實現,有點像
  • 組合模式的實作
  • 在這種情況下,增加新的指令,也就是不會影響執行者,也不會影響客戶。當有新的客戶需要新的命令時,只需要增加命令和請求者。即使有修改的需求,也只是修改請求者。
  • Laravel框架的事件調度機制中,除了觀察者模式外,也很明顯的能看出命令模式的影子

我們的手機工廠和餐廳其實並沒有什麼兩樣,當我們需要代工廠來製作手機時,也是先下訂單,這個訂單就可以看做是命令。在這個訂單中,我們會規定好需要用到的配件,什麼型號的CPU,什麼型號的內存,預裝什麼系統之類的。然後代工廠的工人們就會根據這個訂單來進行生產。在這個過程中,我不用關心是某一個工人還是一群工人來執行這個訂單,我只需要將這個訂單交給和我們對接的人就可以了,然後只管等著手機生產出來進行驗收咯! !

https://github.com/zhangyue0503/designpatterns-php/blob/master/09.command/source/command-up.php

##實例

簡訊功能又回來了,我們發現除了工廠模式外,指令模式看起來也是一種不錯的實作方式哦。在這裡,我們仍然是使用那幾個短信和推送的接口,話不多說,我們用命令模式再來實現一個吧。當然,有興趣的朋友可以接著實現我們的短信撤回功能哈,想想上面的命令取消是怎麼實現的。

簡訊發送類別圖

深入淺析PHP中的指令模式

#https://github.com/zhangyue0503/designpatterns-php/blob/ master/09.command/source/command-message.php

<?php

class SendMsg
{
    private $command = [];

    public function setCommand(Command $command)
    {
        $this->command[] = $command;
    }
    
    public function send($msg)
    {
        foreach ($this->command as $command) {
            $command->execute($msg);
        }
    }
}

abstract class Command
{
    protected $receiver = [];

    public function setReceiver($receiver)
    {
        $this->receiver[] = $receiver;
    }

    abstract public function execute($msg);
}

class SendAliYun extends Command
{
    public function execute($msg)
    {
        foreach ($this->receiver as $receiver) {
            $receiver->action($msg);
        }
    }
}

class SendJiGuang extends Command
{
    public function execute($msg)
    {
        foreach ($this->receiver as $receiver) {
            $receiver->action($msg);
        }
    }
}

class SendAliYunMsg
{
    public function action($msg)
    {
        echo &#39;【阿X云短信】发送:&#39; . $msg, PHP_EOL;
    }
}

class SendAliYunPush
{
    public function action($msg)
    {
        echo &#39;【阿X云推送】发送:&#39; . $msg, PHP_EOL;
    }
}

class SendJiGuangMsg
{
    public function action($msg)
    {
        echo &#39;【极X短信】发送:&#39; . $msg, PHP_EOL;
    }
}

class SendJiGuangPush
{
    public function action($msg)
    {
        echo &#39;【极X推送】发送:&#39; . $msg, PHP_EOL;
    }
}

$aliMsg = new SendAliYunMsg();
$aliPush = new SendAliYunPush();
$jgMsg = new SendJiGuangMsg();
$jgPush = new SendJiGuangPush();

$sendAliYun = new SendAliYun();
$sendAliYun->setReceiver($aliMsg);
$sendAliYun->setReceiver($aliPush);

$sendJiGuang = new SendJiGuang();
$sendAliYun->setReceiver($jgMsg);
$sendAliYun->setReceiver($jgPush);

$sendMsg = new SendMsg();
$sendMsg->setCommand($sendAliYun);
$sendMsg->setCommand($sendJiGuang);

$sendMsg->send(&#39;这次要搞个大活动,快来注册吧!!&#39;);
登入後複製

##

  • 在這個例子中,仍然是多命令多執行者的模式
  • 可以將這個例子與抽象工廠進行對比,同樣的功能使用不同的設計模式來實現,但是要注意的是,抽象工廠更多的是為了生產對象返回對象,而命令模式則是一種行為的選擇
  • 我們可以看出命令模式非常適合形成命令隊列,多命令讓命令可以一條一條執行下去
  • 它允許接收的一方決定是否要否決請求,Receiver做為實現者擁有更多的話語權

原文地址:https://juejin .cn/post/6844903950768930823

作者:硬核心專案經理

推薦學習:《PHP影片教學

以上是深入淺析PHP中的指令模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板