How is SplSubject/SplObserver useful?
标准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 | class MySubject implements SplSubject { protected $_observers = []; public function attach(SplObserver $observer) { $id = spl_object_hash($observer); $this->_observers[$id] = $observer; } public function detach(SplObserver $observer) { $id = spl_object_hash($observer); if (isset($this->_observers[$id])) { unset($this->_observers[$id]); } } public function notify() { foreach ($this->_observers as $observer) { $observer->update($this); } } } class MyObserver implements SplObserver { public function update(SplSubject $subject) { // something happened with $subject, but what // was it??? } } $subject = new MySubject(); $observer = new MyObserver(); $subject->attach($observer); $subject->notify(); |
对于任何现实世界的问题来说,这些接口似乎都是无用的。有人能启发我吗?
编辑:
这是我对接口最大的问题(尽管还有其他问题):
1 | public function update(SplSubject $subject, Event $event) { /* ... */ } |
…捕获以下致命错误:
1 | PHP Fatal error: Declaration of MyObserver::update() must be compatible with SplObserver::update(SplSubject $SplSubject) |
编辑第2页:
通过为附加参数指定默认值,使其成为可选参数,可以防止致命错误,并提供传递上下文的方法,使实现变得值得。我以前没有意识到这一点,所以这几乎回答了我的问题。解决方案是传递您自己的事件/消息数据,并检查它在
界面
当摘要类让你提供一些实现的测量时,界面是纯粹的模板。它只能实现它。用接口关键字声明接口。它可以包括特性和方法声明,但不包括方法机构。
接口使用例
例如,如果你想支持你的项目,应该支持不同的数据库。因此,您可以在将来更改您的数据库,更好地使用包含在档案中的属性程序的界面。
因为你不能创建界面实例,而界面是用于执行面向对象的设计方法EDOCX1[…]4的工具,因为对象定向编程的前期激励是封装的(你不在乎一种能力是如何实现的)。你,作为一个程序员,只在界面上展示。这也是系统结构后观察的好方法
脾主体
正交性是一个虚拟的目标,其中一个目标,作为程序员,应该是构建可以更换或移动的组件,对其他组件的影响最小。
如果你每次改变一个成分都需要一个改变的时间表,那么在编码中,发展任务就可以迅速成为错误创造和消除的螺旋。
因为脾和脾脏两种特征都是一样的
观察模式
观察家模式是一种软件设计模式,它被称为一个对象,称为主体,保持一份附属物清单,称为观察家,并自动通知任何状态的变化,通常是用一种方法。主要用于实现分布式事件处理系统。
- The observer pattern defines an one-to-many dependence between a subject object object and any number of observations so when the subject object object changes state,all its observations are notived and updated a
- The observer pattern essentially allows an unlimited number of objects to observate or listen to events in the observed object(or subject)by registering themselves.在观察家注册参加某一事件之后,当事件着火时,主体将通知他们。
- 当事件发生时,主体通过保存一个观察家的收集和编辑来保存这一手柄,以便通知每一个观察家。
- Observer Pattern Registers Observers with a subject.
- 你可能有多重观察家。Subject must keep a list of registered observers and when event occures it fires(provides notification)all registered observers.
- 当我们不需要任何观察家的时候,也有可能进行登记。
Example 1.贷款利率通知系统
1 2 3 4 5 6 7 | $loan = new Loan("Mortage","Citi Bank", 20.5); $loan->attach(new Online()); $loan->attach(new SMS()); $loan->attach(new Email()); echo"[cc lang="php"]"; $loan->setIntrest(17.5); |
输出
ZZU1
Example 2.简单用户注册监视器
1 2 3 4 5 6 7 8 9 | $users = new Users(); new Audit($users); new Logger($users); new Security($users); $users->addUser("John"); $users->addUser("Smith"); $users->addUser("Admin"); |
输出
1 2 3 4 5 6 7 | Audit : Notify Audit about John Log : User John Create at Wed, 12 Dec 12 12:36:46 +0100 Audit : Notify Audit about Smith Log : User Smith Create at Wed, 12 Dec 12 12:36:46 +0100 Audit : Notify Audit about Admin Log : User Admin Create at Wed, 12 Dec 12 12:36:46 +0100 Security : Alert trying to create Admin |
观测设计模式的优势:主要优势在于物体被称为观测者和可观测者之间的软耦合。The subject only know the list of observers it don't care about how they have their implementation.All the observers are notified by subject in a single event call as broadcast communication
观察设计模式的缺陷:
- 不利之处在于,如果出现任何问题,解答变得非常困难,因为控制的流量在观察者和观察者之间是隐含的,我们可以预测,现在的观察者正在着火,如果观察者之间有链条,那么解答变得更加复杂。
- 另一个问题是与大型观察家交往时的记忆管理。
普通类
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 | abstract class Observable implements SplSubject { protected $_observers = []; public function attach(SplObserver $observer) { $id = spl_object_hash($observer); $this->_observers[$id] = $observer; } public function detach(SplObserver $observer) { $id = spl_object_hash($observer); if (isset($this->_observers[$id])) { unset($this->_observers[$id]); } } public function notify() { foreach ( $this->_observers as $observer ) { $observer->update($this); } } } abstract class Observer implements SplObserver { private $observer; function __construct(SplSubject $observer) { $this->observer = $observer; $this->observer->attach($this); } } |
负载样本类
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 | class Loan extends Observable { private $bank; private $intrest; private $name; function __construct($name, $bank, $intrest) { $this->name = $name; $this->bank = $bank; $this->intrest = $intrest; } function setIntrest($intrest) { $this->intrest = $intrest; $this->notify(); } function getIntrest() { return $this->intrest; } } class Online implements SplObserver { public function update(SplSubject $loan) { printf("Online : Post online about modified Intrest rate of : %0.2f ",$loan->getIntrest()); } } class SMS implements SplObserver { public function update(SplSubject $loan) { printf("Send SMS : Send SMS to premium subscribers : %0.2f ",$loan->getIntrest()); } } class Email implements SplObserver { public function update(SplSubject $loan) { printf("Send Email: Notify mailing list : %0.2f ",$loan->getIntrest()); } } |
用户注册样本类别
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 | class Users extends Observable { private $name; function addUser($name) { $this->name = $name; $this->notify(); } function getName() { return $this->name; } } class Audit extends Observer { public function update(SplSubject $subject) { printf("Audit : Notify Autify about %s ", $subject->getName()); } } class Logger extends Observer { public function update(SplSubject $subject) { printf("Log : User %s Create at %s ", $subject->getName(),date(DATE_RFC822)); } } class Security extends Observer { public function update(SplSubject $subject) { if($subject->getName() =="Admin") { printf("Security : Alert trying to create Admin "); } } } |
这很简单:主题/观察者模式对事件系统不有用。
观察者模式不适合说"这个东西是由x更新的"。相反,它只是说它被更新了。实际上,我已经创建了一个灵活的中介类,可以用于事件系统。根据您的需要,更严格的API可能会有所帮助,但您可以将其作为灵感。
那么主题/观察者模式什么时候有用呢?在更新GUI时,这是一个相当常见的模式,因为某些对象发生了更改。它不需要知道是什么改变了它,也不需要知道为什么改变了它,只需要知道它需要更新。HTTP的本质并不适合这种特定的模式,因为您的PHP代码没有直接绑定到HTML。你必须提出一个新的请求来更新它。
简而言之,主题/观察者模式在PHP中并没有那么有用。此外,该接口没有那么有用,因为您已经使用
这两个接口没有附加的魔力功能,因此实现它们什么也不做。它们实际上只用于参考目的。还有其他类似这样的PHP内部接口,比如
有一些PHP内部接口,如
至于发生了什么,信息不是接口的一部分,因为它不是抽象的。由你来实施。
1 2 3 4 5 6 7 8 9 10 | interface Event extends SplSubject { public function getEventData(); } class MyEvent implements Event { //MySubject implementation above public function getEventData() { return"this kind of event happened"; } } |
您也可以完全忽略
对于一个现实世界的例子,这个链接提供了一个,尽管使用
编辑:我从评论中看到你打算用另一个论点来向
您可以使用可选参数实现更新方法,并且仍然满足SPLSubject接口。
1 2 3 4 5 6 | class MyObserver implements SplObserver { public function update(SplSubject $subject, $eventData = null) { if (is_null($eventData)) // carefull } } |
作为任何接口,它在实现之前都是无用的。通过实现这些,您可以拥有事件驱动的应用程序
假设您有一个事件"applicationstart",您需要在它上面运行10个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function applicationStart() { // Some other logic fnCall1(); fnCall2(); fnCall3(); fnCall4(); fnCall5(); fnCall6(); fnCall7(); fnCall8(); fnCall9(); fnCall10(); // Some other logic } |
现在假设您需要测试这个函数,您将触发对所有其他10个函数的依赖。
如果使用splsubject/splsobserver:
1 2 3 4 5 | function applicationStart() { // Logic $Subject->notify(); // Logic } |
现在,当您测试它时,您只需要确保触发了事件。不执行其他功能。
另外,代码看起来更干净,因为您不会用不属于它的业务逻辑来争论它。还有一个很容易添加触发器的地方
看看https://github.com/thephprigue/event,它做得很好。我认为这是目前最好的包装。我也看不到任何价值
1 | public function notify(/* without args */) { |
在联赛/活动中,您将有以下活动。例如,我有电子邮件列表,希望在新电子邮件添加到列表时处理事件。
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 | class EmailList { const EVENT_ADD_SUBSCRIBER = 'email_list.add_subscriber'; public function __construct($name, $subscribers = []) { // do your stuff $this->emitter = new Emitter(); } /** * Adds event listeners to this list * @param $event * @param $listener */ public function addListener($event, $listener) { $this->emitter->addListener($event, $listener); } /** * Adds subscriber to the list * @param Subscriber $subscriber */ public function addSubscriber(Subscriber $subscriber) { // do your stuff $this->emitter->emit(static::EVENT_ADD_SUBSCRIBER, $subscriber); } } // then in your code $emailList = new EmailList(); $emailList->addListener(EmailList::EVENT_ADD_SUBSCRIBER, function($eventName, $subscriber) { }); |