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();
?>

数据源架构模式之表数据入口

数据源架构模式之表数据入口

【表数据入口的意图】
充当到数据库表的入口的对象。一个实例处理表中的所有行。
表数据入口包含了用于访问单个表或视图的所有SQL,如CRUD等。其它的代码调用它的方法来实现所有与数据库的交互。

【表数据入口的适用场景】
1、表数据入口可以同表模块一起使用。
2、适用于事务脚本处理

【表数据入口的运行机制】
表数据入口的实现非常简单,一般会包括几个从数据库中获取数据的查找方法以及更新,插入和删除方法。
每个方法都将输入参数映射为一个SQL调用并在数据库连接上执行该语句。

【表数据入口的优点和缺点】
1、简单。表数据入口可能是使用起来最简单的数据库接口模式。
2、为数据源的精确访问逻辑提供了一种自然的方法。
3、相同的接口即可以用于SQL操作操作数据库,又可以用于存储过程。

【表数据入口的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
<?php
/**
 * 企业应用架构 数据源架构模式之表数据入口 2010-09-18 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package architecture
 */
 
class PersonGateway {
 
    /**
     * 查询所有人员数据
     * @return <type>
     */
    public function findAll() {
        $sql = "SELECT * FROM person";
        return DB::query($sql);
    }
 
    /**
     * 根据名字查找人员数据
     * @param <type> $name
     * @return <type>
     */
    public function findByName($name) {
        $sql = "SELECT * FROM person WHERE `name` = '" . $name . "'";
        return DB::query($sql);
    }
 
    /**
     * 更新人员数据
     * @param <type> $key   关键字
     * @param <type> $data  需要更新的数据, 例如:$data = array('id' => 1, 'name' => 'Martin', 'birthday' => '');
     */
    public function update($id, $data) {
        if (empty($id) || !is_array($data)) {
            return FALSE;
        }
 
        $sql = "UPDATE person SET ";
        foreach ($data as $field => $value) {
            $sql .= "`" . $field . "` = '" . $value . "',";
        }
        $sql = substr($sql, 0, -1);
 
        $sql .= " WHERE id = " . $id;
 
        return DB::query($sql);
    }
 
    /**
     * 插入人员的数据
     * @param <type> $data 需要写入的数据, 例如:$data = array('name' => 'Martin', 'birthday' => '');
     */
    public function insert($data) {
        if (!is_array($data)) {
            return FALSE;
        }
 
        $sql = "INSERT INTO person ";
        $sql .= "(`" . implode("`,`", array_keys($data)) . "`)";
        $sql .= " VALUES('" . implode("','", array_values($data)) . "')";
 
        return DB::query($sql);
    }
 
}
 
class DB {
 
    /**
     * 这只是一个执行SQL的演示方法
     * @param string $sql   需要执行的SQL
     */
    public static function query($sql) {
        echo "执行SQL: ", $sql, " <br />";
    }
}
 
/**
 * 客户端调用
 */
class Client {
 
     /**
     * Main program.
     */
    public static function main() {
        $person = new PersonGateway();
 
        header("Content-type:text/html; charset=utf-8");
        $data = array('name' => 'Martin', 'birthday' => '2010-09-15');
        $person->insert($data);
 
        $id = 1;
        $data['id'] = $id;
        $person->update($id, $data);
 
        $person->findAll();
 
        $person->findByName('Martin');
    }
}
 
Client::main();
 
?>

如上所示代码仅为一个演示版本
然而这个演示版本有一些可以优化的地方,如:将更新和插入的SQL拼装过程可以提取出来等。

数据表接口是一个很简单的数据源模式,这在我们平常的工作中也有用到,有可能看起来与上面的例子相差较大,但是可能只是将某些方法进行了优化而已,究其本质是一样的。

继续享受我们的生活

最近的烦心的事情挺多的,都是工作上的一些事情。大势所趋,无法抵抗。 可是也有很多自己的问题,觉得情商真是决定了很多东西的,我在改很多方面,可是总觉得不理想。也许是时候换一种工作的方式了?

 

搬家一个多月了,很多好玩的事情。

 

家里的一只肥猫咪咪,老是很好奇地想要往我的卧室钻,像个小孩,越不让去的地方越想去。是一个极懒的家伙,走3步就会卧倒休息,现在开始慢慢不怕我们了,可是老公总希望要保留他在咪咪面前的威严,老去吓它。就好像老想让我小侄女叫他哥哥一样,一厢情愿。

 

原来要步行上班,顺便买早餐,现在都要坐车上班了,就在家里吃早餐。老公晚上看书晚,可是每天早上都要早起我20分钟买早餐,再叫我起床,有的时候还得和赖床的我战斗一小会。感觉到,就算有再多的不如意,每天早上起来,又是幸福的一天,不是吗?

 

换了新的床,不习惯,晚上睡觉做梦,梦到有人来抓我,不停地退。老公发现我往床下滚,过来拉,就更加害怕了,一遍哇哇大叫一遍使劲躲,老公也半睡半醒的也跟着叫。 把掉在半空中的我给捞了起来。 每次想到这个事情就好像笑,半夜两个人哇哇大叫的。

 

有人看过《时间旅行者的妻子》后写了一些她在生活中听到的让人最感动的句子,我看了觉得很甜蜜的,但是真的生活在一起了,不是听着甜言蜜语就可以过的。只要有这种点滴的小感动,就应该积累着,珍惜着,感恩着,享受着生活。