您的位置:首页 > 编程语言 > PHP开发

Php设计模式之【命令模式Command Pattern】

2012-05-19 12:25 841 查看
【案例】

黑枣玩具公司专门生产玩具,生产的玩具不限于狗、猫、狮子等四肢动物。每个玩具身上有两个按钮,分别支持两个操作——“张嘴”与“闭嘴”。

<代码实现>

<?php
/**
 * 所有玩具需要实现的动作接口
 */
interface Toy
{
    /**
     * 玩具张嘴动作
     * @abstract
     * @return mixed
     */
    public function openMouth();

    /**
     * 玩具闭嘴动作
     * @abstract
     * @return mixed
     */
    public function closeMouth();
}

/**
 * 所有玩具上的按钮被按下后需要实现的动作接口
 */
interface Ctrl
{
    /**
     * 按钮1被按下
     * @abstract
     * @return mixed
     */
    public function Bt1Pressed();

    /**
     * 按钮2被按下
     * @abstract
     * @return mixed
     */
    public function Bt2Pressed();
}
/**
 *狗类玩具
 */
class Dog implements Toy,Ctrl
{
    public function openMouth()
    {
        echo "Dog Open Mouth\n";
    }

    public function closeMouth()
    {
        echo "Dog Close Mouth\n";
    }

    public function Bt1Pressed()
    {
        $this->openMouth();
    }

    public function Bt2Pressed()
    {
        $this->closeMouth();
    }
}

class testDriver
{
    public function run()
    {
        //新建一个狗玩具
        $toy = new Dog();
        //狗玩具按钮1被按下
        $toy->Bt1Pressed();
        //狗玩具按钮2被按下
        $toy->Bt2Pressed();
    }
}

$test = new testDriver();
$test->run();
<输出>



【系统要升级】

遥控科技发展,在玩具上的两个控制按钮就不再要了。黑枣玩具公司想要实现手机遥控玩具,

1. 通过设置手机,手机可以与不同玩具连接实现控制

2. 手机星号(*)按钮按键为“张嘴”控制按键

3. 手机井号(#)按钮按键为“闭嘴”控制按键

【分析OOA】

相对于旧版玩具,新的玩具需要实现玩具实体与控制体分离,即玩具狗身上不再有按钮,按钮要分离出来。也其实也是典型的命令模式场景。我们结合一个命令模式分析本案例。

【设计OOD】

<UML>



【说明】

命令模式是有5个角色来组成,分别为

1. 命令角色(Command):声明执行操作的接口。通常代码中表现为接口或者抽象类

在本例中可为玩具命令Command接口

2. 具体命令角色(Concrete Command):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。

在本例中为DogOpenMouthCommand、DogCloseMouthCommand、CatOpenMouthCommand、CatCloseMouthCommand的实例对象。

3. 请求者角色(Invoker):谁去调用命令对象执行这个请求。

谁来让狗猫张嘴闭嘴?当然是手机了。

4. 接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。

在这里接收者为狗或者猫,即Receiver类的一个实例。Receiver们都实现了openMouth张嘴、closeMouth闭嘴操作。

5. 客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。

这里是手机玩家,即Client类的一个实例。

【编程 OOP:

<代码>

1. 先来看看命令接口的定义,示例代码如下:

/**
 * 命令接口
 */
interface Command
{
    /**
     * 执行命令对应的操作
     */
    public function execute();
}


2. 再来看看具体的命令实现对象,示例代码如下:

/**
 * 具体的命令实现对象,示例代码如下:
 */
class OpenMouthCommand implements Command
{
    /**
     * 持有相应的接收者对象
     */
    private $receiver;
    /**
     * 示意,命令对象可以有自己的状态
     */
    private $mouthstate = 0;

    /**
     * 构造方法,传入相应的接收者对象
     * @param receiver 相应的接收者对象
     */
    public function __construct(Receiver $receiver)
    {
        $this->receiver = $receiver;
    }

    public function execute()
    {
        //通常会转调接收者对象的相应方法,让接收者来真正执行功能
        $this->receiver->openMouth();
    }
}

class CloseMouthCommand implements Command
{
    private $receiver;
    private $mouthstate = 0;

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

    public function execute()
    {
        $this->receiver->closeMouth();
    }
}


3. 再来看看接收者对象的实现示意,示例代码如下:

/**
 * 接收者对象超类
 */
abstract class Receiver
{
    /**
     * 示意方法1,真正执行命令相应的操作
     * 在本案例中实现张嘴操作
     */
    abstract function openMouth();

    /**
     * 示意方法2,真正执行命令相应的操作
     * 在本案例中实现闭嘴操作
     */
    abstract function closeMouth();
}

/**
 * 接收者对象:狗
 */
class DogReceiver extends Receiver
{
    public function openMouth()
    {
        echo "Dog open Mouth\n";
    }

    public function closeMouth()
    {
        echo "Dog close Mouth\n";
    }
}

/**
 * 接收者对象:猫
 */
class CatReceiver extends Receiver
{
    public function openMouth()
    {
        echo "Cat open Mouth\n";
    }

    public function closeMouth()
    {
        echo "Cat close Mouth\n";
    }
}


4. 接下来看看Invoker对象,示例代码如下:

/**
 * 调用者
 */
class PhoneInvoker
{
    /**
     * 持有命令对象
     */
    private $_OpenMouthCommand = null;
    private $_CloseMouthComand = null;

    /**
     * 设置调用者持有的命令对象
     * @param command 命令对象
     */
    public function setCommand($opencommand, $closecommand)
    {
        $this->_OpenMouthCommand = $opencommand;
        $this->_CloseMouthComand = $closecommand;
    }

    /**
     * 示意方法,要求命令执行请求
     */
    public function runBtStarPressed()
    {
        $this->_OpenMouthCommand->execute();
    }

    public function runBtHashPressed()
    {
        $this->_CloseMouthComand->execute();
    }
}


5. 再来看看Client的实现,注意这个不是我们通常意义上的测试客户端,主要功能是要创建命令对象并设定它的接收者,因此这里并没有调用执行的代码,示例代码如下:

class Client
{
    protected $receiver;
    protected $invoker;

    //把手机跟玩具连接起来
    public function linkToyAndPhone($receiver, $invoker)
    {
        $this->receiver = $receiver;
        $this->invoker = $invoker;
        $command1 = new OpenMouthCommand($this->receiver);
        $command2 = new CloseMouthCommand($this->receiver);
        //创建Invoker,把命令对象设置进去
        $this->invoker->setCommand($command1, $command2);
    }

    //按下手机*键
    public function pressBtStar()
    {
        $this->invoker->runBtStarPressed();
    }

    //按下手机#键
    public function pressBtHash()
    {
        $this->invoker->runBtHashPressed();
    }
}


【测试用例Test Case】

<代码>

class testDriver
{
    public function run()
    {
        //新建一个玩家
        $client = new Client();
        //新建一个狗玩具
        $dog = new DogReceiver();
        //新建一个猫玩具
        $cat = new CatReceiver();
        //新建一部手机控制端
        $invoker = new PhoneInvoker();
        //玩家把手机连上狗玩具
        $client->linkToyAndPhone($dog, $invoker);
        //按下手机*键
        $client->pressBtHash();
        //按下手机#键
        $client->pressBtStar();
        //玩家把手机连上猫玩具
        $client->linkToyAndPhone($cat, $invoker);
        //按下手机#键
        $client->pressBtHash();
        //按下手机*键
        $client->pressBtStar();

    }
}

$test = new testDriver();
$test->run();


【输出】



【小结】

命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。而在上面的举例中并没有体现出来。其实命令模式之所以能够支持这种操作,完全得益于在请求者与接收者之间添加了中间角色。

为了实现undo功能,首先需要一个历史列表来保存已经执行过的具体命令角色对象;修改具体命令角色中的执行方法,使它记录更多的执行细节,并将自己放入历史列表中;并在具体命令角色中添加undo方法,此方法根据记录的执行细节来复原状态(很明显,首先程序员要清楚怎么来实现,因为它和execute的效果是一样的)。

命令模式还有一个常见的用法就是执行事务操作。这就是为什么命令模式还叫做事务模式的原因吧。它可以在请求被传递到接收者角色之前,检验请求的正确性,甚至可以检查和数据库中数据的一致性,而且可以结合组合模式的结构,来一次执行多个命令。

使用命令模式不仅仅可以解除请求者和接收者之间的耦合,而且可以用来做批处理操作,这完全可以发挥你自己的想象——请求者发出的请求到达命令角色这里以后,先保存在一个列表中而不执行;等到一定的业务需要时,命令模式再将列表中全部的操作逐一执行。

********************************************

* 作者:叶文涛

* 标题:Php设计模式之【命令模式Command Pattern】

* 参考:

*《设计模式:可复用面向对象软件基础 》(美)Erich Gamma 等著

*《Head First设计模式》Eric Freeman等著

*《PHP设计模式》Aaron Saray等著,梁志敏等译(PS:翻译的是狗屁水平)

******************转载请注明网址来源 ***************
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: