php设计模式入门-命令模式

场景:某家公司需要生产一款遥控器,可以控制家用电器的开关,该开关拥有五列两排按键,现在需要我们为这个遥控器编写程序来实现相应功能。

解决方案1看到这样的需求场景,我们第一个想法可能是模拟jsonclick事件,每个按钮按下时触发事件,js调用后台程序执行相应逻辑。

弊端:这样的做法其实是一种硬编码。即各按键一次绑定永不能修改,在这个以人为本尊重用户体验的大环境下这十分的不人性化。

接下我们来看看命令模式怎样为我们解决这个难题:

命令模式的uml类图如下所示:


初看这幅uml图,其实我也觉得乱也没看懂,不过没关系,我们用代码来说明,先看第一个初始版本的设计:


command接口:Command.php

<?php
interface  Command{
    public function execute();
}

电灯类:Light.php

<?php
class Light{
    public function on(){
        echo "the light is on <br/>";
    }
    
    public function off(){
        echo "the light is off <br/>";
    }
}

开灯命令类:LightOffCommand.php

<?php
require_once 'Command.php';
class LightOffCommand implements Command{
    private $_light;
    public function __construct(Light $light){
        $this->_light = $light;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_light->off();
    }

}

关灯命令类:LightOnCommand.php

<?php
require_once 'Command.php';
class LightOnCommand implements Command{
    private $_light;
    public function __construct(Light $light){
        $this->_light = $light;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_light->on();
    }

}

遥控器类:RemoteController.php

<?php
class RemoteController{
    private $_command;
    
    public function setCommand(Command $command){
        $this->_command = $command;
    } 
    
    public function bottonWasPressed(){
        $this->_command->execute();
    }
}

入口文件:index.php

<?php
//初尝命令模式
require_once 'Light.php';
require_once 'LightOffCommand.php';
require_once 'LightOnCommand.php';
require_once 'RemoteController.php';

$light = new Light();
$lightOnCommand = new LightOnCommand($light);
$lightOffCommand = new LightOffCommand($light);

$remoteController = new RemoteController();
$remoteController->setCommand($lightOnCommand);
$remoteController->bottonWasPressed();

$remoteController->setCommand($lightOffCommand);
$remoteController->bottonWasPressed();

可以看出比起硬编码,使用命令模式实现该需求具有更强的灵活性和可变性。

现在我们根据以上的代码再来解释一下上面的uml类图:

Command:声明执行操作的接口。调用接收者相应的操作,以实现执行的方法Execute,对应示例代码中的command.php。

ConcreteCommand:创建一个具体命令对象并设定它的接收者。通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。对应示例代码中的LightOffCommand.php和LightOnCommand.php

Invoker:要求该命令执行这个请求。通常会持有命令对象,可以持有很多的命令对象。对应示例代码中的RemoteController.php

Receiver:知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者,只要它能够实现命令要求实现的相应功能。 对应示例代码中的Light.php

Client:建具体的命令对象,并且设置命令对象的接收者。真正使用命令的客户端是从Invoker来触发执行。 对应示例代码中的index.php


假设有了新的需求,遥控器需要拥有撤销功能,其实很简单,我们只需要在每一个ConcreteCommand类中加上一个undo方法就行,我们现在就来实现这个功能,代码如下:

命令接口:Command.php

<?php
interface  Command{
    public function execute();
}

关灯命令类:LightOffCommand.php

<?php
require_once 'Command.php';
class LightOffCommand implements Command{
    private $_light;
    public function __construct(Light $light){
        $this->_light = $light;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_light->off();
    }
    
    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_light->on();
    }

}

开灯命令类:LightOnCommand.php

<?php
require_once 'Command.php';
class LightOnCommand implements Command{
    private $_light;
    public function __construct(Light $light){
        $this->_light = $light;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_light->on();
    }
    
    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_light->off();
    }

}


电灯类:Light.php

<?php
class Light{
    public function on(){
        echo "the light is on <br/>";
    }
    
    public function off(){
        echo "the light is off <br/>";
    }
}

关闭电风扇命令:CellingFanDownCommand.php

<?php
require_once 'Command.php';
class CellingFanDownCommand implements Command{
    private $_celling_fan;
    public function __construct(CellingFan $cellingFan){
        $this->_celling_fan = $cellingFan;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_celling_fan->down();
    }
    
    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_celling_fan->up();
    }

}

开启电风扇命令:CellingFanUpCommand.php

<?php
require_once 'Command.php';
class CellingFanUpCommand implements Command{
    private $_celling_fan;
    public function __construct(CellingFan $cellingFan){
        $this->_celling_fan = $cellingFan;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_celling_fan->up();
    }

    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_celling_fan->down();
    }
}


电风扇类:CellingFan.php
<?php
class CellingFan{
    public function up(){
        echo "the cellingfan is up <br/>";
    }
    
    public function down(){
         echo "the cellingfan is down <br/>";
    }
}

开车库门命令类:GarageDoorOpenCommand.php

<?php
require_once 'Command.php';
class GarageDoorOpenCommand implements Command{
    private $_garage_door;
    public function __construct(GarageDoor $garageDoor){
        $this->_garage_door = $garageDoor;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_garage_door->open();
    }
    
    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_garage_door->close();
    }

}

关车库门命令:GarageDoorCloseCommand.php

<?php
require_once 'Command.php';
class GarageDoorCloseCommand implements Command{
    private $_garage_door;
    public function __construct(GarageDoor $garageDoor){
        $this->_garage_door = $garageDoor;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        $this->_garage_door->close();
    }
    
    public function undo()
    {
        // TODO Auto-generated method stub
        $this->_garage_door->open();
    }

}
车库门类:GarageDoor.php
<?php
class GarageDoor{
    public function open(){
        echo "the garagedoor is open <br/>";
    }
    
    public function close(){
        echo "the garagedoor is close <br/>";
    }
}

什么都不做的命令类:NoCommand.php

<?php
require_once 'Command.php';
class NoCommand implements Command{
    
    public function execute()
    {
        
    }

}

遥控器类:RemoteController.php

<?php
require_once 'NoCommand.php';

class RemoteController
{

    private $_onCommand = array();

    private $_offCommand = array();

    private $_undoCommand;

    public function __construct()
    {
        for ($i = 1; $i < 7; $i ++) {
            $this->_onCommand[$i] = new NoCommand();
            $this->_offCommand[$i] = new NoCommand();
        }
        $this->_undoCommand = new NoCommand();
    }

    public function setOnAndOffCommand($key, Command $onCommand, Command $offCommand)
    {
        $this->_onCommand[$key] = $onCommand;
        $this->_offCommand[$key] = $offCommand;
    }

    public function onBottonWasPressed($key)
    {
        $this->_onCommand[$key]->execute();
        $this->_undoCommand = $this->_onCommand[$key];
    }

    public function offBottonWasPressed($key)
    {
        $this->_offCommand[$key]->execute();
        $this->_undoCommand = $this->_offCommand[$key];
    }
    
    public function undoBottonWasPressed()
    {
        $this->_undoCommand->undo();
    }
}

入口文件:index.php

<?php
//初尝命令模式
require_once 'Light.php';
require_once 'LightOffCommand.php';
require_once 'LightOnCommand.php';
require_once 'GarageDoor.php';
require_once 'GarageDoorCloseCommand.php';
require_once 'GarageDoorOpenCommand.php';
require_once 'CellingFan.php';
require_once 'CellingFanDownCommand.php';
require_once 'CellingFanUpCommand.php';
require_once 'RemoteController.php';

$light = new Light();
$lightOnCommand = new LightOnCommand($light);
$lightOffCommand = new LightOffCommand($light);

$garageDoor = new GarageDoor();
$garageDoorOpenCommand = new GarageDoorOpenCommand($garageDoor);
$garageDoorCloseCommand = new GarageDoorCloseCommand($garageDoor);

$cellingFan = new CellingFan();
$cellingFanUpCommand = new CellingFanUpCommand($cellingFan);
$cellingFanDownCommand = new CellingFanDownCommand($cellingFan);

$remoteController = new RemoteController();
$remoteController->setOnAndOffCommand(0, $lightOnCommand, $lightOffCommand);
$remoteController->setOnAndOffCommand(1, $garageDoorOpenCommand, $garageDoorCloseCommand);
$remoteController->setOnAndOffCommand(2, $cellingFanUpCommand, $cellingFanDownCommand);

$remoteController->onBottonWasPressed(0);
$remoteController->offBottonWasPressed(0);
$remoteController->onBottonWasPressed(1);
$remoteController->offBottonWasPressed(1);
$remoteController->onBottonWasPressed(2);
$remoteController->offBottonWasPressed(2);
上面的代码中出现了一个NoCommand.php,如果没有设计这个类,当我们试图去调用没有被声明的命令类时,程序会报错,比如说我们去调用
$remoteController->onBottonWasPressed(3);
因为并未对键值为3的$_onCommand进行赋值,程序将会报错,而现在初始化时每个键值被赋予为NoCommand命令类,就可以规避这种情形下的报错


我们再扩展一下,如果用户希望可以一键执行打开点电灯、打开风扇、打开车库门这一系列的操作怎么办呢?我们可以设计一组宏命令绑定在遥控器的某个按钮上供用户使用,具体实现代码如下所示:

宏命令类:MacroCommand.php

<?php
require_once 'Command.php';
class MacroCommand implements Command{
    private $_commands;
    
    public function __construct(array $commands){
        $this->_commands = $commands;
    }
	/* (non-PHPdoc)
     * @see Command::execute()
     */
    public function execute()
    {
        // TODO Auto-generated method stub
        foreach ($this->_commands as $command){
            $command->execute();
        }
    }
}

入口文件index.php变更为:

<?php
// 初尝命令模式
require_once 'Light.php';
require_once 'LightOffCommand.php';
require_once 'LightOnCommand.php';
require_once 'GarageDoor.php';
require_once 'GarageDoorCloseCommand.php';
require_once 'GarageDoorOpenCommand.php';
require_once 'CellingFan.php';
require_once 'CellingFanDownCommand.php';
require_once 'CellingFanUpCommand.php';
require_once 'RemoteController.php';
require_once 'MacroCommand.php';

$light = new Light();
$lightOnCommand = new LightOnCommand($light);
$lightOffCommand = new LightOffCommand($light);

$garageDoor = new GarageDoor();
$garageDoorOpenCommand = new GarageDoorOpenCommand($garageDoor);
$garageDoorCloseCommand = new GarageDoorCloseCommand($garageDoor);

$cellingFan = new CellingFan();
$cellingFanUpCommand = new CellingFanUpCommand($cellingFan);
$cellingFanDownCommand = new CellingFanDownCommand($cellingFan);

$macroOnCommand = new MacroCommand(array(
    $lightOnCommand,
    $garageDoorOpenCommand,
    $cellingFanUpCommand
));
$macroOffCommand = new MacroCommand(array(
    $lightOffCommand,
    $garageDoorCloseCommand,
    $cellingFanDownCommand
));

$remoteController = new RemoteController();
/* $remoteController->setOnAndOffCommand(0, $lightOnCommand, $lightOffCommand);
$remoteController->setOnAndOffCommand(1, $garageDoorOpenCommand, $garageDoorCloseCommand);
$remoteController->setOnAndOffCommand(2, $cellingFanUpCommand, $cellingFanDownCommand);

$remoteController->onBottonWasPressed(0);
$remoteController->undoBottonWasPressed();
$remoteController->offBottonWasPressed(0);
$remoteController->undoBottonWasPressed();
$remoteController->onBottonWasPressed(1);
$remoteController->undoBottonWasPressed();
$remoteController->offBottonWasPressed(1);
$remoteController->undoBottonWasPressed();
$remoteController->onBottonWasPressed(2);
$remoteController->undoBottonWasPressed();
$remoteController->offBottonWasPressed(2);
$remoteController->undoBottonWasPressed(); */

$remoteController->setOnAndOffCommand(3, $macroOnCommand, $macroOffCommand);
$remoteController->onBottonWasPressed(3);
$remoteController->offBottonWasPressed(3);






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值