标签归档:读书总结

PHP设计模式笔记:使用PHP实现观察者模式

PHP设计模式笔记:使用PHP实现观察者模式

【意图】
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新【GOF95】
又称为发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听(Source-Listener)模式、或从属者(Dependents)模式

【观察者模式结构图】

观察者模式

观察者模式

【观察者模式中主要角色】
抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。抽象主题提供了增加和删除观察者对象的接口。
抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
具体主题(ConcreteSubject)角色:存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
具体观察者(ConcretedObserver)角色:存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。

【观察者模式的优点和缺点】
观察者模式的优点:
1、观察者和主题之间的耦合度较小;
2、支持广播通信;

观察者模式的缺点:
1、由于观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。这可能会引起意外的更新。

【观察者模式适用场景】
1、当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。
2、当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
3、当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。

【观察者模式与其它模式】
中介者模式(Mediator):通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者。
单例模式(singleton模式):ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问的。

【观察者模式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
<?php
 
/**
 * 观察者模式 2010-09-23 sz
 * @author phppan.p#gmail.com  http://www.phppan.com                                             
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 抽象主题角色
 */
interface Subject {
 
    /**
     * 增加一个新的观察者对象
     * @param Observer $observer
     */
    public function attach(Observer $observer);
 
    /**
     * 删除一个已注册过的观察者对象
     * @param Observer $observer
     */
    public function detach(Observer $observer);
 
    /**
     * 通知所有注册过的观察者对象
     */
    public function notifyObservers();
}
 
/**
 * 具体主题角色
 */
class ConcreteSubject implements Subject {
 
    private $_observers;
 
    public function __construct() {
        $this->_observers = array();
    }
 
    /**
     * 增加一个新的观察者对象
     * @param Observer $observer
     */
    public function attach(Observer $observer) {
        return array_push($this->_observers, $observer);
    }
 
    /**
     * 删除一个已注册过的观察者对象
     * @param Observer $observer
     */
    public function detach(Observer $observer) {
        $index = array_search($observer, $this->_observers);
        if ($index === FALSE || ! array_key_exists($index, $this->_observers)) {
            return FALSE;
        }
 
        unset($this->_observers[$index]);
        return TRUE;
    }
 
    /**
     * 通知所有注册过的观察者对象
     */
    public function notifyObservers() {
        if (!is_array($this->_observers)) {
            return FALSE;
        }
 
        foreach ($this->_observers as $observer) {
            $observer->update();
        }
 
        return TRUE;
    }
 
}
 
/**
 * 抽象观察者角色
 */
interface Observer {
 
    /**
     * 更新方法
     */
    public function update();
}
 
class ConcreteObserver implements Observer {
 
    /**
     * 观察者的名称
     * @var <type>
     */
    private $_name;
 
    public function __construct($name) {
        $this->_name = $name;
    }
 
    /**
     * 更新方法
     */
    public function update() {
        echo 'Observer', $this->_name, ' has notified.<br />';
    }
 
}
 
/**
 * 客户端
 */
class Client {
 
    /**
     * Main program.
     */
    public static function main() {
        $subject = new ConcreteSubject();
 
        /* 添加第一个观察者 */
        $observer1 = new ConcreteObserver('Martin');
        $subject->attach($observer1);
 
        echo '<br /> The First notify:<br />';
        $subject->notifyObservers();
 
        /* 添加第二个观察者 */
        $observer2 = new ConcreteObserver('phppan');
        $subject->attach($observer2);
 
        echo '<br /> The Second notify:<br />';
        $subject->notifyObservers();
 
        /* 删除第一个观察者 */
        $subject->detach($observer1);
 
        echo '<br /> The Third notify:<br />';
        $subject->notifyObservers();
    }
 
}
 
Client::main();
?>

PHP设计模式笔记:使用PHP实现模板方法模式

PHP设计模式笔记:使用PHP实现模板方法模式

【意图】
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以在不改变一个算法的结构的情况下重定义该算法的某些特定的步骤【GOF95】

【模板方法模式结构图】

Template Method

Template Method

【模板方法模式中主要角色】
抽象模板(AbstractClass)角色: 定义一个或多个抽象方法让子类实现。这些抽象方法叫做基本操作,它们是顶级逻辑的组成部分。
定义一个模板方法。这个模板方法一般是一个具体方法,它给出顶级逻辑的骨架,而逻辑的组成步骤在对应的抽象操作中,这些操作将会推迟到子类中实现。同时,顶层逻辑也可以调用具体的实现方法

具体模板(ConcrteClass)角色:实现父类的一个或多个抽象方法,作为顶层逻辑的组成而存在。

每个抽象模板可以有多个具体模板与之对应,而每个具体模板有其自己对抽象方法(也就是顶层逻辑的组成部分)的实现,从而使得顶层逻辑的实现各不相同。

【模板方法模式适用场景】
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
3、控制子类扩展。

【模板方法模式与其它模式】
1、策略模式(strategy模式):模板方法使用继承来改变算法的部分,策略模式使用委托来改变整个算法。区别在于封闭的变化不同,一个变化的部分,一个变化的是整体。
2、工厂方法模式(factory method模式):Factory Method模式常被模板方法调用。
【模板方法模式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
<?php
/**
 * 模板方法模式简单示例 2010-09-12 sz
 * @author phppan.p#gmail.com  http://www.phppan.com                                                       
 * 哥学社成员(http://www.blog-brother.com/)
 * @package design pattern
 */
 
/**
 * 抽象模板角色
 * 定义抽象方法作为顶层逻辑的组成部分,由子类实现
 * 定义模板方法作为顶层逻辑的架子,调用基本方法组装顶层逻辑
 */
abstract class AbstractClass {
 
    /**
     * 模板方法 调用基本方法组装顶层逻辑
     */
    public function templateMethod() {
        echo 'templateMethod begin.<br />';
        $this->primitiveOperation1();
        $this->primitiveOperation2();
        echo 'templateMethod end.<br />';
    }
 
    /**
     * 基本方法1
     */
    abstract protected function primitiveOperation1();
 
     /**
     * 基本方法2
     */
    abstract protected function primitiveOperation2();
}
 
/**
 * 具体模板角色
 * 实现父类的抽象方法
 */
class ConcreteClass extends AbstractClass{
    /**
     * 基本方法1
     */
    protected function primitiveOperation1() {
        echo 'primitiveOperation1<br />';
    }
 
     /**
     * 基本方法2
     */
    protected function primitiveOperation2(){
        echo 'primitiveOperation2<br />';
    }
 
}
 
/**
 * 客户端
 */
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
        $class = new ConcreteClass();
        $class->templateMethod();
    }
}
 
Client::main();
?>

【模板方法模式】
模板方法是一种代码复用的基本技术,模板方法导致一种反射的控制结构,这指的是一个父类调用子类的操作。
其实现过程:准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。

【重构的原则】
重构时应当遵守的原则是:将行为以是移到结构的高端,而将状态尽量移动到结构的低端。
Auer曾在文献【AUER95】中指出:
1、应当要所行为而不是状态定义一个类。
2、在实现行为是,是用抽象状态而不是用具体状态。
3、给操作划分层次。
4、将状态的确认推迟到子类中。在父类中,如果需要状态属性的话,可以调用抽象的取值方法,而将抽象的取值方法的实现放到具体子类中。
如果可以遵守以上的而,那么就可以在等级结构中将接口与实现分离,将抽象与具体分离,从而保证代码可以最大限度的被复用。

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模式)有相似之处。