1.不是示例代码,而是实际应用代码. 2.多个观察者,多个被观察者 3.根据业务情况,观察者与被观察者都是单例的. 统称为 业务逻辑处理单元(Unit) 1.先实现 处理单元的基类 ,主要实现单例 <?php /** * 所有业务逻辑处理单元的基类,实现了单例化 * @author bluehire * */ class SUnit { /** * 禁止实例化 */ protected function __construct(){ } /** * 获取本类单例的方法,公开 * * @return SUnit */ public static function instance() { //延迟绑定的句柄,子类单例,而不是基类 if(static::$handle){ return static::$handle; } //这个是延迟绑定的,运行时的子类的类名 $class = get_called_class(); static::$handle = new $class(); return static::$handle; } } 2.现具体实现一个 观察者(以日志为例,这个是最常用的,而且足够简单) 这里对普通观察者进行了一次扩展,允许带一个参数 原因解释一下: 同一个被观察者可能在业务的不同阶段通知同一个观察者, 我想明确告知 观察者 当前阶段之类的信息, 本来可以用被观察者对象的属性来实现,但感觉太绕了,不明确. <?php /** * 文本日志处理单元,观察者模式 * @author bluehire * */ class ULog extends SUnit implements SplObserver { protected static $handle; //单例句柄 /** * 得到了被观察者的一个通知 * @see SplObserver::update() */ public function update(SplSubject $object,$params=null){ //需要被观察者提供一个日志文件名 $file=$object->getLogFile($params); //需要被观察者提供一个日志内容 $msg=$object->getLogMsg($params); //具体干活 writeLog($file, $msg); } } 3.根据以上,如果一个被观察者 要想 被 日志观察者 观察(这话可真费劲), 就要实现两个方法, 所以,这就导致了一个接口 <?php /** * 所有需要日志观察者的被观察者要实现的接口 * @author bluehire * */ interface ILogSubject extends SplSubject{ /** * 要提供日志文件名 */ public function getLogFile($params=null); /** * 要提供日志内容 */ public function getLogMsg($params=null); } 4.现在终于轮到 被观察者了, 所有被观察者都有三个 固定方法,我实现到基类中 <?php /** * 被观察者(观察者设计模式)的基类,父类为业务逻辑处理单元(实现了单例化) * @author bluehire * */ class SSubject extends SUnit{ //当前被观察者的所有观察者 private $observers=array(); /** * 增加一个观察者 * @param SplObserver $observer * @return SSubject */ public function attach(SplObserver $observer){ $this->observers[]=$observer; return $this; } /** * 取消一个观察者,这个不常用 * @param SplObserver $observer * @return SSubject */ public function detach(SplObserver $observer){ $idx = array_search ( $observer, $this->observers, true ); if ($idx) { unset ( $this->observers [$idx] ); } return $this; } /** * 通知所有观察者 * @param 可以带一个参数,发送给所有观察者,观察者会将此参数用于回调被观察者的方法 * @return SSubject */ public function notify($params=null){ foreach ( $this->observers as $observer ) { $observer->update ( $this,$params ); } return $this; } } 5.开始具体的被观察者,以订单为例 <?php /** * 订单处理单元,作为 被观察者 * @author bluehire * */ class UOrder extends SSubject implements ILogSubject{ protected static $handle; //单例句柄 /** * 单例, * @see SUnit::instance() * @return UOrder */ public static function instance(){ //附加文本日志观察者 parent::instance()->attach(ULog::instance()); return self::$handle; } //所有观察者 private $observers=array(); /** * 为日志观察者提供日志文件名 * @see ILogSubject::getLogFile() */ public function getLogFile($params=null){ return 'order'; } /** * 为日志观察者提供日志内容 * @see ILogSubject::getLogMsg() */ public function getLogMsg($params=null){ return $params; } public function something(){ //...... $this->notify(array('a'=>1)); } } 以上程序实测通过,部分代码请自行修改(如dump,writeLog), 现在说适用范围: 设计 模式这东西,绝对不是随便就可以用的, 简单业务逻辑使用这东西就是个找死.我这里也是因为业务复杂到应该使用一部分设计模式 多个观察者,多个被观察者 才需要使用这个模式 , 未完事项: 我仍在纠结,要不要使用观察者模式, 相比 过程化的消息通知机制( 被观察者 逐个 通知 观察者,使用 设计 好的 接口), 体会不到好处. 欢迎讨论 Q:31008088
观察者模式实践应用php,PHP 观察者模式 的真正实现
最新推荐文章于 2021-03-19 16:02:33 发布