SplSubject代表着被观察的对象,其结构:

  1. interface SplSubject{
  2. //添加(注册)一个观察者
  3. public function attach(SplObserver $observer);
  4. //删除一个观察者
  5. public function detach(SplObserver $observer);
  6. //当状态发生改变时,通知所有观察者
  7. public function notify();
  8. }

SplSubject 接口中的方法:

方法声明 描述
abstract public void attach ( SplObserver $observer ) 添加(注册)一个观察者
abstract public void detach ( SplObserver $observer ) 删除一个观察者
abstract public void notify ( void ) 当状态发生改变时,通知所有观察者

SplObserver 代表着充当观察者的对象,其结构:

  1. interface SplObserver{
  2. //在目标发生改变时接收目标发送的通知;当关注的目标调用其notify()时被调用
  3. public function update(SplSubject $subject);
  4. }
SplObserver 中的方法:
方法声明 描述
abstract public void update ( SplSubject $subject ) 在目标发生改变时接收目标发送的通知;当关注的目标调用其notify()时被调用

<说明>
该设计模式的核心思想是, SplSubject 维护了一个特定的状态,在其状态改变时会调用 notify() 方法,一旦这个方法被调用,任何先前通过 attach() 方法注册上来的 SplObserver 对象都会以调用其 update() 方法的方式被更新。

为什么使用 SplObjectStorage 类?

SplObjectStorage 类实现了以对象为键的映射(map)或对象的集合(如果忽略作为键的对象所对应的数据)这种数据结构。这个类的实例很像一个数组,但是它所存放的对象都是唯一的。这个特点就为快速实现 Observer 设计模式贡献了不少力量,因为我们不希望同一个观察者被注册多次。该类的另一个特点是,可以直接从中删除指定的对象,而不需要遍历或搜索整个集合。

SplObjectStorage::attach() 方法的部分源代码:

  1. function attach($obj, $inf = NULL)
  2. {
  3. if (is_object($obj) && !$this->contains($obj))
  4. {
  5. $this->storage[] = array($obj, $inf);
  6. }
  7. }

SplObjectStorage类的实例之所以能够只存储唯一的对象,是因为其 SplObjectStorage::attach()方法的实现中先判断了指定的对象是否已经被存储:

  1. <?php
  2. /**
  3. * this is the base class
  4. */
  5. class Base
  6. {
  7. function __construct()
  8. {
  9. echo 'base Class';
  10. }
  11. }
  12. class MyObserver implements SplObserver
  13. {
  14. public function update(SplSubject $subject)
  15. {
  16. echo "MyObserver2 updated\n";
  17. print_r($this->getUserInfo($subject));
  18. }
  19. public function getUserInfo($subject)
  20. {
  21. return $subject->getUserInfo();
  22. }
  23. }
  24. class User extends Base implements SplSubject {
  25. private $_email;
  26. private $_username;
  27. private $_mobile;
  28. private $_password;
  29. private $_userInfo;
  30. /**
  31. * @var SplObjectStorage
  32. */
  33. private $observers = NULL;
  34. public function __construct($email, $username, $mobile, $password) {
  35. $this->_email = $email;
  36. $this->_username = $username;
  37. $this->_mobile = $mobile;
  38. $this->_password = $password;
  39. $this->observers = new SplObjectStorage();
  40. }
  41. public function attach(SplObserver $observer) {
  42. $this->observers->attach($observer);
  43. }
  44. public function detach(SplObserver $observer) {
  45. $this->observers->detach($observer);
  46. }
  47. public function notify() {
  48. foreach ($this->observers as $observer) {
  49. $observer->update($this);
  50. }
  51. }
  52. public function setUserInfo()
  53. {
  54. $userInfo = array(
  55. 'username' => $this->_username,
  56. 'password' => $this->_password,
  57. 'email' => $this->_email,
  58. 'mobile' => $this->_mobile,
  59. );
  60. $this->_userInfo = $userInfo;
  61. }
  62. public function getUserInfo()
  63. {
  64. return $this->_userInfo;
  65. }
  66. public function create() {
  67. echo __METHOD__, PHP_EOL;
  68. $this->setUserInfo();
  69. $this->notify();
  70. }
  71. public function changePassword($newPassword) {
  72. echo __METHOD__, PHP_EOL;
  73. $this->_password = $newPassword;
  74. $this->setUserInfo();
  75. $this->notify();
  76. }
  77. public function resetPassword() {
  78. echo __METHOD__, PHP_EOL;
  79. $this->_password = mt_rand(100000, 999999);
  80. $this->setUserInfo();
  81. $this->notify();
  82. }
  83. }
  84. class EmailSender implements SplObserver {
  85. public function update(SplSubject $subject) {
  86. $userInfo = $subject->getUserInfo(); //调用被观察者的函数
  87. echo "向 {$userInfo['email']} 发送电子邮件成功。内容是:你好 {$userInfo['username']}" .
  88. "你的新密码是 {$userInfo['password']},请妥善保管", PHP_EOL;
  89. }
  90. }
  91. class MobileSender implements SplObserver {
  92. public function update(SplSubject $subject) {
  93. $userInfo = $subject->getUserInfo();
  94. echo "向 {$userInfo['email']} 发送短信成功。内容是:你好 {$userInfo['username']},你的新密码是 {$userInfo['password']},请妥善保管", PHP_EOL;
  95. }
  96. }

测试用例Test Case:
<代码>

  1. class testDriver
  2. {
  3. public function run()
  4. {
  5. //observer
  6. $observer = new MyObserver();
  7. $subject = new User('user@domain.com', '张三', '13610002000', '123456');
  8. $subject->attach($observer);
  9. $subject->notify();
  10. //email
  11. $email_sender = new EmailSender();
  12. $mobile_sender = new MobileSender();
  13. $user = new User('user@domain.com', '张三', '13610002000', '123456');
  14. // 创建用户时通过 Email 和手机短信通知用户
  15. $user->attach($email_sender);
  16. $user->attach($mobile_sender);
  17. $user->create($user);
  18. $user->resetPassword($user);
  19. echo PHP_EOL;
  20. }
  21. }
  22. $test = new testDriver();
  23. $test->run();

分类: web

标签:   php