PHP设计模式-观察者模式(订阅者模式)
2013-07-22 22:24
721 查看
相信大家都用过QQ(没用过QQ的大叔不要扔我),而且大家都很讨厌QQ的小弹窗,不时地就会跳出一个小窗口,真心烦人。那么如果我们是腾讯消息推送的服务端开发人员。如果要用PHP来实现这种消息发送那么如果做到呢?
方案一。被动推送方式
我们采用推的方式来接收消息。也说说,由服务端向各位用户直接推送消息。我们考虑地简单一点,毕竟我们只是学习设计模式嘛。首先,我们需要有一个用户类。可以展示推送的消息。其次,我们需要一个消息推送器的类,有个推送消息的方法,它可以指定把消息推给哪个用户。
好,需要的东西都有了。那么,该把消息推给谁呢?嗯,有了,我先把要推送消息的用户都存在一个列表中。当有新消息需要推送的时候,我直接推送给每个用户就可以了。于是,把推送器稍做修改如下:
推送器可以通过pushAll()方法给所有用户推送消息了。我们发现,我们还缺少一些东西,对,我们需要一些把用户从推送器添加或删除的方法。同时,我们需要注意一下,用户需要一个唯一的标识来区分是哪个用户。于是在用户类中添加一个用户标识userId,并且,创建用户时需指定这个Id.而推送器在添加和删除用户时,都会使用这个Id。代码如下:
推送方式的代码完成了。现在我们开始测试一下,给两个用户发送消息。
我们看到,推送器创建了一个数组,用来存所有需要推送消息的用户。其实我们可以把这些用户叫做观察者,或者叫订阅者。把它们加入到这个数组中,就表示他们对这个消息器中的消息很关心。那么消息推送器在有新消息的时候,就遍历这个数组,把消息推送出去。
方案二。拉取方式
如果这个用户量很大呢?数组存不下怎么办?如果用户不在线,消息也会推送不出去。那这种推的方式就不适用了。
我们就会想了,那我们改成拉的模式吧。这样我们不用维护大的用户列表,也不用关心用户在不在线了。当用户上线后,可以连接到消息器上获取消息。
要实现拉的模式。首先用户还是要有显示消息的方法,但不同之处在于,它需要知道消息器是什么?那么消息器就简单了,要有一个可以获取消息的方法。并且有一个消息器的唯一标识(messageId)如下:
在上面,用户可以支持从一个消息器拉消息。那么,如果有多个消息器呢?我们需要在用户中保存一个推送器的列表,然后定时遍历推送器列表,获取消息。于是最终代码如下:
拉模式的代码我们也完成了。现在来测试一下效果:
从上面推拉模式两种代码我们可以看出,它们的区别在于,一个是在消息器中存储用户列表,另一个是在用户类中存储消息器列表,要获取和推送消息时,都要遍历这个列表进行推送和拉取消息。
对于拉模式而言,它只需维护感兴趣的消息器列表。但它并不知道,消息器什么时候会有新消息。它只能定时地去试探是否有新消息过来。而且,每个用户都必须维护一个消息器列表。所以会重复地试探性拉数据。效率不高。但消息器服务端服务都是正常的,只要用户去取消息,那么基本上都能正常取到,因此更稳定些。故而,两种模式要根据不同的业务情况合理使用。
方案一。被动推送方式
我们采用推的方式来接收消息。也说说,由服务端向各位用户直接推送消息。我们考虑地简单一点,毕竟我们只是学习设计模式嘛。首先,我们需要有一个用户类。可以展示推送的消息。其次,我们需要一个消息推送器的类,有个推送消息的方法,它可以指定把消息推给哪个用户。
class User { //展示推送过来的消息 public function showMessage($msg) { echo "Message: $msg".PHP_EOL; } } class Messager { //推送消息给某用户 public function push(User $user, String $msg) { $user->showMessage($msg); } }
好,需要的东西都有了。那么,该把消息推给谁呢?嗯,有了,我先把要推送消息的用户都存在一个列表中。当有新消息需要推送的时候,我直接推送给每个用户就可以了。于是,把推送器稍做修改如下:
class Messager { //用来存储需要推送消息的用户 protected $_users = array(); //推送消息给某用户 public function push(User $user, String $msg) { $user->showMessage($msg); } //将消息推送给所有用户 public function pushAll($msg) { foreach ($this->_users as $user) { $this->push($user, $msg); } } }
推送器可以通过pushAll()方法给所有用户推送消息了。我们发现,我们还缺少一些东西,对,我们需要一些把用户从推送器添加或删除的方法。同时,我们需要注意一下,用户需要一个唯一的标识来区分是哪个用户。于是在用户类中添加一个用户标识userId,并且,创建用户时需指定这个Id.而推送器在添加和删除用户时,都会使用这个Id。代码如下:
class User { //用户的唯一标识 private $_userId; //用户初始化时需指定ID public function __construct($userId) { $this->_userId = $userId; } //获取用户ID public function getUserId() { return $this->_userId; } //展示推送过来的消息 public function showMessage($msg) { echo "Message: $msg".PHP_EOL; } } class Messager { //用来存储需要推送消息的用户 protected $_users = array(); //推送消息给某用户 public function push(User $user, $msg) { $user->showMessage($msg); } //将消息推送给所有用户 public function pushAll($msg) { foreach ($this->_users as $user) { $this->push($user, $msg); } } //添加用户 public function addUser(User $user) { $this->_users[$user->getUserId()] = $user; } //删除用户 public function delUser($userId) { unset($this->_users[$userId]); } //清除所有用户 public function clearUsers() { $this->_users = array(); } }
推送方式的代码完成了。现在我们开始测试一下,给两个用户发送消息。
$messager = new Messager(); $user1 = new User(1); $user2 = new User(2); $messager->addUser($user1); $messager->addUser($user2); $messager->pushAll("test");
我们看到,推送器创建了一个数组,用来存所有需要推送消息的用户。其实我们可以把这些用户叫做观察者,或者叫订阅者。把它们加入到这个数组中,就表示他们对这个消息器中的消息很关心。那么消息推送器在有新消息的时候,就遍历这个数组,把消息推送出去。
方案二。拉取方式
如果这个用户量很大呢?数组存不下怎么办?如果用户不在线,消息也会推送不出去。那这种推的方式就不适用了。
我们就会想了,那我们改成拉的模式吧。这样我们不用维护大的用户列表,也不用关心用户在不在线了。当用户上线后,可以连接到消息器上获取消息。
要实现拉的模式。首先用户还是要有显示消息的方法,但不同之处在于,它需要知道消息器是什么?那么消息器就简单了,要有一个可以获取消息的方法。并且有一个消息器的唯一标识(messageId)如下:
class User { //用户的唯一标识 private $_userId; //用户初始化时需指定ID public function __construct($userId) { $this->_userId = $userId; } //获取用户ID public function getUserId() { return $this->_userId; } //展示消息推送器的消息,它需要传递进来一个消息器 public function showMessage(Messager $messager) { echo "Message".$messager->getMessage(); } } class Messager { //消息器Id private $_messagerId; //消息器初始化时需要指定Id public function __construct($messagerId) { $this->_messagerId = $messagerId; } //获取Messager的Id public function getMessagerId() { return $this->_messagerId; } //从远端获取消息信息 public function getMessage() { //此处实现从服务端拿消息 ... return "远端拿到的消息"; } }
在上面,用户可以支持从一个消息器拉消息。那么,如果有多个消息器呢?我们需要在用户中保存一个推送器的列表,然后定时遍历推送器列表,获取消息。于是最终代码如下:
class User { //用户的唯一标识 private $_userId; //推送器列表 protected $_messagers; //用户初始化时需指定ID public function __construct($userId) { $this->_userId = $userId; } //获取用户ID public function getUserId() { return $this->_userId; } //添加消息器 public function addMessager(Messager $messager) { $this->_messagers[$messager->getMessagerId()] = $messager; } //移除消息器 public function removeMessager($messagerId) { unset($this->_messagers[$messagerId]); } //清除消息器 public function clearMessagers() { $this->_messagers = array(); } //显示所有消息并显示 public function showAllMessage() { foreach ($this->_messagers as $messager) { $this->showMessage($messager); } } //展示消息推送器的消息,它需要传递进来一个消息器 public function showMessage(Messager $messager) { echo "Message".$messager->getMessage(); } } class Messager { //消息器Id private $_messagerId; //消息器初始化时需要指定Id public function __construct($messagerId) { $this->_messagerId = $messagerId; } //获取Messager的Id public function getMessagerId() { return $this->_messagerId; } //从远端获取消息信息 public function getMessage() { //此处实现从服务端拿消息 ... return "远端拿到的消息"; } }
拉模式的代码我们也完成了。现在来测试一下效果:
$user = new User(1); $messager1 = new Messager(1); $messager2 = new Messager(2); $user->addMessager($messager1); $user->addMessager($messager2); $user->showAllMessage();
从上面推拉模式两种代码我们可以看出,它们的区别在于,一个是在消息器中存储用户列表,另一个是在用户类中存储消息器列表,要获取和推送消息时,都要遍历这个列表进行推送和拉取消息。
对于拉模式而言,它只需维护感兴趣的消息器列表。但它并不知道,消息器什么时候会有新消息。它只能定时地去试探是否有新消息过来。而且,每个用户都必须维护一个消息器列表。所以会重复地试探性拉数据。效率不高。但消息器服务端服务都是正常的,只要用户去取消息,那么基本上都能正常取到,因此更稳定些。故而,两种模式要根据不同的业务情况合理使用。
相关文章推荐
- PHP设计模式-观察者模式(订阅者模式)
- PHP设计模式-观察者模式(订阅者模式)
- php 设计模式 观察者observer模式
- php设计模式--观察者模式
- php设计模式-观察者模式
- php设计模式学习之观察者模式
- php模式设计之 观察者模式
- 学习PHP设计模式之观察者模式
- php模式设计之 观察者模式
- PHP设计模式:观察者模式
- PHP设计模式之观察者模式
- php 设计模式(观察者模式)
- php设计模式之观察者模式
- PHP设计模式-观察者模式
- PHP设计模式之观察者模式(Observer)详细介绍和代码实例
- PHP设计模式之观察者模式
- 设计模式-观察者模式(PHP实现)
- [置顶] php设计模式之观察者模式
- php 设计模式-观察者模式
- php设计模式(3)-观察者模式