`
lanlansnss
  • 浏览: 44570 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

设计模式-观察者模式

    博客分类:
  • php
 
阅读更多
<?php
/**
 * 观察者模式
 * 实现用户登录 并且 记录下log 发送mail如果ip在匹配iplist设置cookie
 * 
 */

interface Observable{
		function attach( Observer $observer );
		function detach( Observer $observer );
		function notify();
}

class Login implements  Observable {
	private $observers = array();
	const LOGIN_USER_UNKNOW = 1;
	const LOGIN_WRONG_PASS  = 2;
	const LOGIN_ACESS       = 3;
	private $status   = array();
	function attach( Observer $observer ){
		$this->observers[] = $observer;
	}
	function detach( Observer $observer ){
		$newobservers = array();
		foreach ( $this->observers as $obs ){
			if( $obs !== $observer){
				$newobservers[] = $obs;
			}
		}
		$this->observers = $newobservers;
	} 
	function notify(){
		foreach ( $this->observers as $obs ){
			$obs->update( $this );
		}
	}
	/**
	 * 登录启动器
	 * 记录登录状态
	 * return BOOLEAN
	 * 
	 * */
	function handleLogin( $user, $pass, $ip ){
		switch ( rand(1,3) ) {
			case 1:
				$this->setStatus( self::LOGIN_ACESS , $user, $ip );
				$ret = TRUE;
				break;
			case 2:
				$this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
				$ret = FALSE;
				break;
			case 3:
				$this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );
				$ret = FALSE;
				break;
		}
		$this->notify();
		return $ret;
	}
	/**
	 * 记录登录状态
	 *
	 * @param unknown_type $status
	 * @param unknown_type $user
	 * @param unknown_type $ip
	 */
	private function setStatus( $status, $user, $ip ){
		$this->status = array( $status, $user, $ip );
	}
	/**
	 * 获得登录状态
	 *
	 * @return unknown
	 */
	function getStatus(){
		return $this->status;
	}
}
/*定义Observer*/
interface Observer{
	function update( Observable $observable );
}
abstract class LoginObserver implements Observer {
	private $login;
	function __construct( Login $login ) {
		$this->login = $login;
		$login->attach( $this );
	}
	function update( Observable $observable ){
		if( $observable === $this->login ){
			$this->doUpdate( $observable );
		}
	}
	abstract function  doUpdate( Login $login );
}
/**
 * 登录错误发邮件通知用户
 *
 */
class SecurityMonitor extends LoginObserver {
	function doUpdate ( Login $login  ){
		$status = $login->getStatus();
		if( $status[0] === LOGIN::LOGIN_WRONG_PASS ){
			print __CLASS__."\tsend mail to sysadmin\n";
		}
	}
}
/**
 *记录log日志
 *
 */
class GeneralLogger extends LoginObserver {
	function doUpdate( Login $login ){
		$status = $login->getStatus();
		print __CALSS__.":\t add login data to log\n";
	}
}
/**
 * 登录ip匹配iplist
 * 设置cookie
 *
 */
class PartnershipTool extends LoginObserver {
	function doUpdate( Login $login ){
		$status = $login->getStatus();
		print __CLASS__.":\t set cookie of ip match alist \n";
	}
}
$login = new Login();
new SecurityMonitor( $login );
new GeneralLogger( $login );
new PartnershipTool( $login );

$login->handleLogin('snuser','s','s');


php可以通过内置的Spl(standard php library php标准类)提供观察者模式的原生支持。
SPl是一套可以帮助程序员处理很多面向对象的工具
其中观察者(obServer)由三个元素组成:SplObserver , SplSubject , SplObjectStrorage
其中SploOserver和SplSubject都是接口和上面所说的Observer 和 Observable接口完全相同
SplObjectStorage是一个工具类, 用于处理存储对象和删除对象。

下面就是改进后的示例代码
<?php
/**
 * 使用php内部的对象工具,
 * 完成观察者模式
 * SplObserver
 * SplSubject
 * SplObjectStorage工具类用于存储对象和删除对象
 * 实现用户登录 并且 记录下log 发送mail如果ip在匹配iplist设置cookie
 * 
 */

class Login implements SplSubject {
	private $storage;
	const LOGIN_USER_UNKNOW = 1;
	const LOGIN_WRONG_PASS  = 2;
	const LOGIN_ACESS       = 3;
	private $status   = array();
	function __construct(){
		$this->storage = new SplObjectStorage();
	}
	function attach( SplObserver $observer ){
		$this->storage->attach( $observer );
	}
	function detach( SplObserver $observer ){
		$this->storage->detach($observer);
	}
	function notify(){
		foreach ( $this->storage as $obs ){
			$obs->update( $this );
		}
	}
		/**
	 * 登录启动器
	 * 记录登录状态
	 * return BOOLEAN
	 * 
	 * */
	function handleLogin( $user, $pass, $ip ){
		switch ( rand(1,3) ) {
			case 1:
				$this->setStatus( self::LOGIN_ACESS , $user, $ip );
				$ret = TRUE;
				break;
			case 2:
				$this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
				$ret = FALSE;
				break;
			case 3:
				$this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );
				$ret = FALSE;
				break;
		}
		$this->notify();
		return $ret;
	}
	/**
	 * 记录登录状态
	 *
	 * @param unknown_type $status
	 * @param unknown_type $user
	 * @param unknown_type $ip
	 */
	private function setStatus( $status, $user, $ip ){
		$this->status = array( $status, $user, $ip );
	}
	/**
	 * 获得登录状态
	 *
	 * @return unknown
	 */
	function getStatus(){
		return $this->status;
	}
	
}
abstract class LoginObserver implements SplObserver {
	private $login;
	function __construct( Login $login ){
		$this->login = $login;
		$login->attach( $this );
	}
	function update( SplSubject $subject ){
		if( $subject === $this->login ){
			$this->doUpdate( $subject );
		}
	}
	abstract function doUpdate( Login $login );
}
/**
 * 登录错误发邮件通知用户
 *
 */
class SecurityMonitor extends LoginObserver {
	function doUpdate ( Login $login  ){
		$status = $login->getStatus();
		if( $status[0] === LOGIN::LOGIN_WRONG_PASS ){
			print __CLASS__."\tsend mail to sysadmin\n";
		}
	}
}
/**
 *记录log日志
 *
 */
class GeneralLogger extends LoginObserver {
	function doUpdate( Login $login ){
		$status = $login->getStatus();
		print __CALSS__.":\t add login data to log\n";
	}
}
/**
 * 登录ip匹配iplist
 * 设置cookie
 *
 */
class PartnershipTool extends LoginObserver {
	function doUpdate( Login $login ){
		$status = $login->getStatus();
		print __CLASS__.":\t set cookie of ip match alist \n";
	}
}
$login = new Login();
new SecurityMonitor( $login );
new GeneralLogger( $login );
new PartnershipTool( $login );
$login->detach(new PartnershipTool( $login ));
$login->handleLogin('user','s','s');



引用文章http://blog.csdn.net/swengineer/article/details/6268244

是一个观察者模式的案例。
/**
 * 观察者模式应用场景实例
 *
 * 免责声明:本文只是以哈票网举例,示例中并未涉及哈票网任何业务代码,全部原创,如有雷同,纯属巧合。
 *
 * 场景描述:
 * 哈票以购票为核心业务(此模式不限于该业务),但围绕购票会产生不同的其他逻辑,如:
 * 1、购票后记录文本日志
 * 2、购票后记录数据库日志
 * 3、购票后发送短信
 * 4、购票送抵扣卷、兑换卷、积分
 * 5、其他各类活动等
 *
 * 传统解决方案:
 * 在购票逻辑等类内部增加相关代码,完成各种逻辑。
 *
 * 存在问题:
 * 1、一旦某个业务逻辑发生改变,如购票业务中增加其他业务逻辑,需要修改购票核心文件、甚至购票流程。
 * 2、日积月累后,文件冗长,导致后续维护困难。
 *
 * 存在问题原因主要是程序的"紧密耦合",使用观察模式将目前的业务逻辑优化成"松耦合",达到易维护、易修改的目的,
 * 同时也符合面向接口编程的思想。
 *
 * 观察者模式典型实现方式:
 * 1、定义2个接口:观察者(通知)接口、被观察者(主题)接口
 * 2、定义2个类,观察者对象实现观察者接口、主题类实现被观者接口
 * 3、主题类注册自己需要通知的观察者
 * 4、主题类某个业务逻辑发生时通知观察者对象,每个观察者执行自己的业务逻辑。
 *
 * 示例:如以下代码
 *
 */

/**
 * *
 * 买票
 * 记录购买信息
 * 送优惠卷
 * 短信日志记录
 * 文本日志
 * 
 */
由于原文是使用JAVA来实现 , 下面我用phpSPL工具 来实现观察者模式 实现这个案例
/**
 * 构建主题, 用于发送通知
 * 
 * 
 * */
class buyTicket implements SplSubject  {
	private $ticket;
	private $storage;
	function __construct (){
		$this->storage = new SplObjectStorage();
	}
	function attach( SplObserver $object ){
		$this->storage->attach($object);
	}
	function detach( SplObserver $object ){
		$this->storage->detach($object);
	}
	function notify(){
		foreach ( $this->storage as $obs ){
			$obs->update( $this );
		}
	}
	/**
	 * 买票成功 
	 * 发送通知记录日志
	 * 送折扣劵
	 *
	 * @return buyTicket
	 */
	function buyTicket(){
		$this->ticket = 'NO.12, $100.';
		$this->notify();
	}
	function getTicket(){
		return $this->ticket;
	}
}
/**
 * 接收通知的观察者
 * 判断通知来源, 并且相应观察者执行相应的操作
 *
 */
abstract class  ticketObject implements SplObserver {
	private $buyTicket;
	function __construct( buyTicket $buyTicket ){
		$this->buyTicket = $buyTicket;
		$buyTicket->attach( $this );
	}
	function update( SplSubject $subject ) {
		if( $subject === $this->buyTicket ){
			$this->doUpdate( $subject );
		}
	}
	abstract  function doUpdate( SplSubject $buyTicket );
}
/**
 * 
 * 日志观察者接到通知记录日志
 *
 */
class ticketLogger  extends  ticketObject {
	function doUpdate( SplSubject $buyTicket ){
		print __CLASS__.' 日志计入 买票 '.$buyTicket->getTicket()."\n";
	}
	
}
/**
 * 
 * 折扣劵观察者 接到通知发送折扣劵
 *
 */
class ticketDid  extends ticketObject {
	function doUpdate( SplSubject $buyTicket ){
		print __CLASS__."\t 送10元折扣卷一张".$buyTicket->getTicket();
	}
}
$ticket = new buyTicket();
new ticketLogger( $ticket );
new ticketDid( $ticket );
$ticket->buyTicket();

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics