PHP设计模式笔记:使用PHP实现命令模式

PHP设计模式笔记:使用PHP实现命令模式

【意图】
将一个请求封装为一个对象,从而使用你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
可变的方面是:何时,怎样满足一个请求
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

【命令模式结构图】

Command模式

Command模式

【命令模式中主要角色】
命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色。
具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现Execute()方法,负责调用接收考的相应操作。Execute()方法通常叫做执行方法。
客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者。
请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

【命令模式的优点】
命令模式的优点:
1、命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分离开。
2、命令类与其他任何别的类一样,可以修改和推广。
3、可以把命令对象聚合在一起,合成为合成命令。
4、可以很容易的加入新的命令类。
命令模式的缺点:可能会导致某些系统有过多的具体命令类。

【命令模式适用场景】
1、抽象出待执行的动作以参数化对象。Command模式是回调机制的一个面向对象的替代品。
2、在不同的时刻指定、排列和执行请求。
3、支持取消操作。
4、支持修改日志。
5、用构建在原语操作上的高层操作构造一个系统。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

【命令模式与其它模式】
合成模式(composite模式):Composite模式可被实现宏命令
原型模式(prototype模式):如果命令类带有clone(或在之前的文章中提到的copy方法)方法,命令就可以被复制。在命令模式支持多次取消操作时可能需要用到此模式,以复制当前状态的Command对象

【命令模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?php
/**
 * 命令模式 2010-08-21 sz
 * @author phppan.p#gmail.com  http://www.phppan.com                                                       
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 命令角色
 */
interface Command {
 
    /**
     * 执行方法
     */
    public function execute();
}
 
/**
 * 具体命令角色
 */
class ConcreteCommand implements Command {
 
    private $_receiver;
 
    public function __construct(Receiver $receiver) {
        $this->_receiver = $receiver;
    }
 
    /**
     * 执行方法
     */
    public function execute() {
        $this->_receiver->action();
    }
}
 
/**
 * 接收者角色
 */
class Receiver {
 
    /* 接收者名称 */
    private $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    /**
     * 行动方法
     */
    public function action() {
        echo $this->_name, ' do action.<br />';
    }
}
 
/**
 * 请求者角色
 */
class Invoker {
 
    private $_command;
 
    public function __construct(Command $command) {
        $this->_command = $command;
    }
 
    public function action() {
        $this->_command->execute();
    }
}
 
/**
 * 客户端
 */
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
        $receiver = new Receiver('phpppan');
        $command = new ConcreteCommand($receiver);
        $invoker = new Invoker($command);
        $invoker->action();
    }
}
 
Client::main();
 
?>

【命令模式协作】
1、Client创建一个ConcreteCommand对象并指定它的Receiver对象
2、某Invoker对象存储该ConcreteCommand对象
3、该Invoker通过调用Command对象的execute操作来提交一个请求。若该命令是可撤消的,ConcreteCommand就在执行execute操作之前存储当前状态以用于取消命令。
4、ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。

【宏命令】
在这里,我们以一个简单的增加和粘贴功能为例,将这两个命令组成一个宏命令。
我们可以新建复制命令和粘贴命令,然后将其添加到宏命令中去。
如下所示代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
 
<?php
/**
 * 命令模式之宏命令 2010-08-22 00:10  sz
 * @author phppan.p#gmail.com  http://www.phppan.com                                                   
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 命令角色
 */
interface Command {
 
    /**
     * 执行方法
     */
    public function execute();
}
 
/**
 * 宏命令接口
 */
interface MacroCommand extends Command {
 
    /**
     *  宏命令聚集的管理方法,可以删除一个成员命令
     * @param Command $command
     */
    public function remove(Command $command);
 
    /**
     * 宏命令聚集的管理方法,可以增加一个成员命令
     * @param Command $command
     */
    public function add(Command $command);
 
}
 
 
class DemoMacroCommand implements MacroCommand {
 
    private $_commandList;
 
    public function __construct() {
        $this->_commandList = array();
    }
 
    public function remove(Command $command) {
        $key = array_search($command, $this->_commandList);
        if ($key === FALSE) {
            return FALSE;
        }
 
        unset($this->_commandList[$key]);
        return TRUE;
    }
 
    public function add(Command $command) {
        return array_push($this->_commandList, $command);
    }
 
    public function execute() {
        foreach ($this->_commandList as $command) {
            $command->execute();
        }
 
    }
 
}
 
/**
 * 具体拷贝命令角色
 */
class CopyCommand implements Command {
 
    private $_receiver;
 
    public function __construct(Receiver $receiver) {
        $this->_receiver = $receiver;
    }
 
    /**
     * 执行方法
     */
    public function execute() {
        $this->_receiver->copy();
    }
}
 
/**
 * 具体拷贝命令角色
 */
class PasteCommand implements Command {
 
    private $_receiver;
 
    public function __construct(Receiver $receiver) {
        $this->_receiver = $receiver;
    }
 
    /**
     * 执行方法
     */
    public function execute() {
        $this->_receiver->paste();
    }
}
 
/**
 * 接收者角色
 */
class Receiver {
 
    /* 接收者名称 */
    private $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    /**
     * 复制方法
     */
    public function copy() {
        echo $this->_name, ' do copy action.<br />';
    }
 
    /**
     * 粘贴方法
     */
    public function paste() {
        echo $this->_name, ' do paste action.<br />';
    }
}
 
/**
 * 请求者角色
 */
class Invoker {
 
    private $_command;
 
    public function __construct(Command $command) {
        $this->_command = $command;
    }
 
    public function action() {
        $this->_command->execute();
    }
}
 
/**
 * 客户端
 */
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
        $receiver = new Receiver('phpppan');
        $pasteCommand = new PasteCommand($receiver);
        $copyCommand = new CopyCommand($receiver);
 
        $macroCommand = new DemoMacroCommand();
        $macroCommand->add($copyCommand);
        $macroCommand->add($pasteCommand);
 
        $invoker = new Invoker($macroCommand);
        $invoker->action();
 
        $macroCommand->remove($copyCommand);
        $invoker = new Invoker($macroCommand);
        $invoker->action();
    }
}
 
Client::main();
 
 
?>

个人觉得在命令的委派操作上,与访问者模式(Visitor模式)有相似之处。

PHP帮助手册拾遗一:运算符

PHP帮助手册拾遗一:运算符

1、基本的赋值运算符是“=”。一开始可能会以为它是“等于”,其实不是的。
它实际上意味着把右边表达式的值赋给左边的运算数。

赋值运算表达式的值也就是所赋的值。也就是说,“$a = 3”的值是 3。这样就可以做一些小技巧:

1
2
3
4
5
<?php
 
$a = ($b = 4) + 5; // $a 现在成了 9,而 $b 成了 4。     
 
?>

2、取模 $a % $b 在 $a 为负值时的结果也是负值。
3、 尽管 ! 比 = 的优先级高,PHP 仍旧允许类似如下的表达式:if (!$a = foo()),在此例中 foo() 的输出被赋给了 $a。
同样,如下代码:

1
2
3
4
5
<?php
 if ($a = 100 && $b = 200) {                  
var_dump($a, $b); 
} 
?>

先赋值,再进行与运算
此问题鸟哥有一篇文章有谈到过,猛击http://www.laruence.com/2010/07/26/1668.html

4、如果比较一个整数和字符串,则字符串会被转换为整数。
如果比较两个数字字符串,则作为整数比较。此规则也适用于 switch 语句。

5、执行运算符
PHP 支持一个执行运算符:反引号(“)。注意这不是单引号!PHP 将尝试将反引号中的内容作为外壳命令来执行,并将其输出信息返回(例如,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符“`”的效果与函数 shell_exec() 相同。

1
2
3
4
<?php
$output = `ls -al`;                                           
echo $output;
?>

注: 反引号运算符在激活了安全模式或者关闭了 shell_exec() 时是无效的。

6、递增/递减运算符不影响布尔值。递减 NULL 值也没有效果,但是递增 NULL 的结果是 1。

1
2
3
4
5
6
$b = TRUE;
echo --$b;                                                                          
echo ++$b;
$c = NULL;
echo --$c;
echo ++$c;

输出在个1和一个空值

7、+ 运算符把右边的数组附加到左边的数组后面,但是重复的键值不会被覆盖。

1
2
3
4
$array1 = array(1, 'key' => 2);
$array2 = array('key' => 3, 'key1' => 4);                                 
$array = $array1 + $array2;
var_dump($array);

输出array(3) { [0]=> int(1) ["key"]=> int(2) ["key1"]=> int(4) }
数组2中的3已经不存在了,数组1中的2没有被覆盖

8、使用括号可以增强代码的可读性。

9、除号(“/”)总是返回浮点数,除非这两个运算数是整数(或由字符串转换成的整数)
这里手册中有一点没有说明,返回的值必须是整数才行,否则如 6/4的结果就是float了
//此处在某个中文手册中有翻译错误,意思完全弄反了,纠结…
代码示例:

1
2
3
4
5
6
7
8
9
$a = 8;
$b = 4;
$c = $a / $b;
var_dump($c);                                                                     
 
$a = 6;
$b = 4;
$c = $a / $b;
var_dump($c);

10、空字符串,false,NULL等值在算术运算符中将会以0对待。
如下所示代码:

1
2
3
echo 5 * "", '<br>';                                                                
echo 5 + false, '<br>';
echo 5 / NULL, '<br>';

11、 加号、减号、点号运行的优先级是一样一样滴
如下所示代码:

1
2
$var = 3;
echo "string " . $var + 3;

输出3

12、instanceof后面不能接字符串,可以直接接类名或包含类名的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Demo {                                                                     
 
}
 
$demo = new Demo();
 
/* 以变量的形式存储类名字符串是正确的 */
$class = "Demo";
if ($demo instanceof $class) {
    echo 'yes';
}
 
/* 不能直接接类名的字符串,即类名不能加双引号 */
$demo = new Demo();
 
if ($demo instanceof "Demo") {
    echo 'yes';
}
 
/* 类名上无引号 */
$demo = new Demo();
 
if ($demo instanceof Demo) {
    echo 'yes';
}

PHP设计模式笔记:使用PHP实现合成模式

PHP设计模式笔记:使用PHP实现合成模式

【意图】
将对象组合成树形结构以表示”部分-整体”的层次结构。Composite使用户对单个对象和组合对象的使用具有一致性。
Composite变化的是一个对象的结构和组成

【合成模式中主要角色】
抽象组件(Component)角色:抽象角色,给参加组合的对象规定一个接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子组件
树叶组件(Leaf)角色:在组合中表示叶节点对象,叶节点没有子节点。在组合中定义图元对象的行为。
树枝组件(Composite)角色:存储子部件。定义有子部件的那些部件的行为。在Component接口中实现与子部件有关的操作。
客户端(Client):通过Component接口操纵组合部件的对象

【合成模式的优点和缺点】
Composite模式的优点
1、简化客户代码
2、使得更容易增加新类型的组件

Composite模式的缺点:使你的设计变得更加一般化,容易增加组件也会产生一些问题,那就是很难限制组合中的组件

【合成模式适用场景】
1、你想表示对象的部分-整体层次结构
2、你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象。

【合成模式与其它模式】
装饰器模式:Decorator模式经常与Composite模式一起使用。当装饰与合成一起使用时,它们通常有一个公共的父类。因此装饰必须支持具有add,remove和getChild操作的Component接口
享元模式:Flyweight模式让你共享组件,但不再引用他们的父部件
迭代器模式:Itertor可用来遍历Composite
访问者模式:Visitor将本来应该分布在Composite和Leaf类中的操作和行为局部化。

【安全式的合成模式】
在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的。因为树叶类型的对象根本就没有管理子类的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。编译通不过,就不会出现运行时期错误
这样的缺点是不够透明,因为树叶类和合成类将具有不同的接口。

【安全式的合成模式结构图】

安全式的合成模式

安全式的合成模式


【安全式的合成模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 
<?php
 
/**
 * 安全的合成模式的PHP实现 2010-08-10 sz
 * @author 胖子 phppan.p#gmail.com  http://www.phppan.com                                        
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
 
/**
 * 抽象组件角色
 */
interface Component {
 
    /**
     * 返回自己的实例
     */
    public function getComposite();
 
    /**
     * 示例方法
     */
    public function operation();
}
 
/**
 * 树枝组件角色
 */
class Composite implements Component {
    private $_composites;
 
    public function __construct() {
        $this->_composites = array();
    }
 
    public function getComposite() {
        return $this;
    }
 
    /**
     * 示例方法,调用各个子对象的operation方法
     */
    public function operation() {
        echo 'Composite operation begin:<br />';
        foreach ($this->_composites as $composite) {
            $composite->operation();
        }
        echo 'Composite operation end:<br /><br />';
    }
 
    /**
     * 聚集管理方法 添加一个子对象
     * @param Component $component  子对象
     */
    public function add(Component $component) {
        $this->_composites[] = $component;
    }
 
    /**
     * 聚集管理方法 删除一个子对象
     * @param Component $component  子对象
     * @return boolean  删除是否成功
     */
    public function remove(Component $component) {
        foreach ($this->_composites as $key => $row) {
            if ($component == $row) {
                unset($this->_composites[$key]);
                return TRUE;
            }
        }
 
        return FALSE;
    }
 
    /**
     * 聚集管理方法 返回所有的子对象
     */
    public function getChild() {
       return $this->_composites;
    }
 
}
 
class Leaf implements Component {
    private $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    public function operation() {
        echo 'Leaf operation ', $this->_name, '<br />';
    }
 
    public function getComposite() {
        return null;
    }
}
 
 
/**
 * 客户端
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
        $leaf1 = new Leaf('first');
        $leaf2 = new Leaf('second');
 
        $composite = new Composite();
        $composite->add($leaf1);
        $composite->add($leaf2);
        $composite->operation();
 
        $composite->remove($leaf2);
        $composite->operation();
    }
 
}
 
Client::main();
?>

【透明式的合成模式】
在Composite类里面声明所有的用来管理子类对象的方法。这样做的是好处是所有的组件类都有相同的接口。在客户端看来,树叶类和合成类对象的区别起码在接口层次上消失了,客户端可以同等的对待所有的对象。这就是透明形式的合成模式
缺点就是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此调用其添加或删除方法就没有意义了,这在编译期间是不会出错的,而只会在运行时期才会出错。

【透明式的合成模式结构图】

透明式的合成模式

透明式的合成模式

【透明式的合成模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
 
<?php
 
/**
 * 透明的合成模式的PHP实现 2010-08-10 sz
 * @author 胖子 phppan.p#gmail.com  http://www.phppan.com                                               
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
 
/**
 * 抽象组件角色
 */
interface Component {
 
    /**
     * 返回自己的实例
     */
    public function getComposite();
 
    /**
     * 示例方法
     */
    public function operation();
 
      /**
     * 聚集管理方法 添加一个子对象
     * @param Component $component  子对象
     */
    public function add(Component $component);
 
    /**
     * 聚集管理方法 删除一个子对象
     * @param Component $component  子对象
     * @return boolean  删除是否成功
     */
    public function remove(Component $component);
 
    /**
     * 聚集管理方法 返回所有的子对象
     */
    public function getChild();
 
}
 
/**
 * 树枝组件角色
 */
class Composite implements Component {
    private $_composites;
 
    public function __construct() {
        $this->_composites = array();
    }
 
    public function getComposite() {
        return $this;
    }
 
    /**
     * 示例方法,调用各个子对象的operation方法
     */
    public function operation() {
        echo 'Composite operation begin:<br />';
        foreach ($this->_composites as $composite) {
            $composite->operation();
        }
        echo 'Composite operation end:<br /><br />';
    }
 
    /**
     * 聚集管理方法 添加一个子对象
     * @param Component $component  子对象
     */
    public function add(Component $component) {
        $this->_composites[] = $component;
    }
 
    /**
     * 聚集管理方法 删除一个子对象
     * @param Component $component  子对象
     * @return boolean  删除是否成功
     */
    public function remove(Component $component) {
        foreach ($this->_composites as $key => $row) {
            if ($component == $row) {
                unset($this->_composites[$key]);
                return TRUE;
            }
        }
 
        return FALSE;
    }
 
    /**
     * 聚集管理方法 返回所有的子对象
     */
    public function getChild() {
       return $this->_composites;
    }
 
}
 
class Leaf implements Component {
    private $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    public function operation() {
        echo 'Leaf operation ', $this->_name, '<br />';
    }
 
    public function getComposite() {
        return null;
    }
 
      /**
     * 聚集管理方法 添加一个子对象,此处没有具体实现,仅返回一个FALSE
     * @param Component $component  子对象
     */
    public function add(Component $component) {
        return FALSE;
    }
 
    /**
     * 聚集管理方法 删除一个子对象
     * @param Component $component  子对象
     * @return boolean  此处没有具体实现,仅返回一个FALSE
     */
    public function remove(Component $component) {
        return FALSE;
    }
 
    /**
     * 聚集管理方法 返回所有的子对象 此处没有具体实现,返回null
     */
    public function getChild() {
       return null;
    }
 
}
 
 
/**
 * 客户端
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
        $leaf1 = new Leaf('first');
        $leaf2 = new Leaf('second');
 
        $composite = new Composite();
        $composite->add($leaf1);
        $composite->add($leaf2);
        $composite->operation();
 
        $composite->remove($leaf2);
        $composite->operation();
 
    }
 
}
 
Client::main();
?>

可以看到透明式合成模式的Leaf有各聚集管理方法的平庸实现