`

隔墙有耳的观察者模式(Observer Patern)

阅读更多

登录系统想必大家都做过,验证用户名密码就登录成功,日志系统应该记录此次登录,如果登录出错,安全系统应该会记录此次错误,邮件系统也应该会发送相关邮件给管理员,等等。这就好像登录系统被很多人监视一样,一旦有什么风吹草动,立即会被其它系统获悉。那就用观察者模式来试试,类图如下:



很简单的模式,实现代码:

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


class login implements Observable{
	const LOGIN_USER_UNKNOW = 1;
	const LOGIN_WRONG_PASS = 2;
	const LOGIN_ACCESS = 3;
	private $status = array();
	private $observers = array();

	public function setStatus( $status, $user, $ip ) {
		$this->status = array( $status, $user, $ip );
	}
	public function getStatus() {
		return $this->status;
	}
	public function handleLogin( $user, $pass, $ip ) {
		switch ( mt_rand( 1, 3 ) ) {
		case 1:
			$this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );
			$ret = false;
			break;
		case 2:
			$this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
			$ret = false;
			break;
		case 3:
			$this->setStatus( self::LOGIN_ACCESS, $user, $ip );
			$ret = true;
			break;
		}
		$this->notify();
		return $ret;
	}


	public function attach( Observer $observer ) {
		$this->observers[] = $observer;
	}

	public function detach( Observer $observer ) {
		$newObservers = array();
		foreach ( $this->observers as $obs ) {
			if ( $obs !== $observer )
				$newObservers[] = $obs;
		}
		$this->observers = $newObservers;
	}

	public function notify() {
		foreach ( $this->observers as $obs ) {
			$obs->update( $this );
		}
	}
}

interface Observer{
	function update( Observable $observable );
}

class SecurityMonitor implements Observer{
	function update( Observable $observable ) {
		$status = $observable->getStatus();
		if($status[0] == Login::LOGIN_WRONG_PASS){
			echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";
		}
	}
}

$login = new Login();
$login->attach(new SecurityMonitor());
$login->handleLogin('XXX','XXX','127.0.0.1');
?>
出错时的运行结果:
SecurityMonitor:XXX于127.0.0.1登录失败[Finished in 0.1s]

代码中可以看到login对象主动添加SecurityMonitor对象观察。这样要调用Login::getStatus(),SecurityMonitor类就必须了解更多信息。虽然调用发生于一个ObServable对象上,但无法保证对象也是一个Login对象。为解决这个问题,有一个办法:断续保持ObServable接口的通用性,由ObServer类负责保证它们的主体是正确的类型。它们甚至能将自己添加到主体上。类图如下:



实现代码如下:

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


class login implements Observable{
	const LOGIN_USER_UNKNOW = 1;
	const LOGIN_WRONG_PASS = 2;
	const LOGIN_ACCESS = 3;
	private $status = array();
	private $observers = array();

	public function setStatus( $status, $user, $ip ) {
		$this->status = array( $status, $user, $ip );
	}
	public function getStatus() {
		return $this->status;
	}
	public function handleLogin( $user, $pass, $ip ) {
		switch ( mt_rand( 1, 3 ) ) {
		case 1:
			$this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );
			$ret = false;
			break;
		case 2:
			$this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
			$ret = false;
			break;
		case 3:
			$this->setStatus( self::LOGIN_ACCESS, $user, $ip );
			$ret = true;
			break;
		}
		$this->notify();
		return $ret;
	}


	public function attach( Observer $observer ) {
		$this->observers[] = $observer;
	}

	public function detach( Observer $observer ) {
		$newObservers = array();
		foreach ( $this->observers as $obs ) {
			if ( $obs !== $observer )
				$newObservers[] = $obs;
		}
		$this->observers = $newObservers;
	}

	public function notify() {
		foreach ( $this->observers as $obs ) {
			$obs->update( $this );
		}
	}
}

interface Observer{
	function update( Observable $observable );
}
//以上代码和上例是一样的
abstract class LoginObserver implements Observer{
	private $login;
	public function __construct( Login $login ) {
		$this->login = $login;
		$login->attach( $this );
	}

	public function update( Observable $observable ) {
		if ( $this->login === $observable )
			$this->doUpdate( $observable );
	}
	abstract function doUpdate( Login $login );
}

class SecurityMonitor extends LoginObserver{
	public function doUpdate( Login $login ) {
		$status = $login->getStatus();
		if ( $status[0] == Login::LOGIN_WRONG_PASS )
			echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";
	}
}

$login = new Login();
new SecurityMonitor($login);//此外login对象是被动被观察的
$login->handleLogin( 'XXX', 'XXX', '127.0.0.1' );
?>
运行结果与上例子相同

在php5后,内置的SPL扩展提供了对观察者模式的原生支持。将上例子通过SPL改进后:

<?php
class login implements SplSubject{
	const LOGIN_USER_UNKNOW = 1;
	const LOGIN_WRONG_PASS = 2;
	const LOGIN_ACCESS = 3;
	private $status = array();
	// private $observers = array();
	private $storage;
	public function __construct() {
		$this->storage = new SplObjectStorage();
	}
	public function setStatus( $status, $user, $ip ) {
		$this->status = array( $status, $user, $ip );
	}
	public function getStatus() {
		return $this->status;
	}
	public function handleLogin( $user, $pass, $ip ) {
		switch ( mt_rand( 1, 3 ) ) {
		case 1:
			$this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );
			$ret = false;
			break;
		case 2:
			$this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
			$ret = false;
			break;
		case 3:
			$this->setStatus( self::LOGIN_ACCESS, $user, $ip );
			$ret = true;
			break;
		}
		$this->notify();
		return $ret;
	}


	public function attach( SplObserver $observer ) {
		$this->storage->attach( $observer );
	}

	public function detach( SplObserver $observer ) {
		$this->storage->detach( $observer );
	}

	public function notify() {
		foreach ( $this->storage as $obs ) {
			$obs->update( $this );
		}
	}
}

abstract class LoginObserver implements SplObserver{
	private $login;
	public function __construct( Login $login ) {
		$this->login = $login;
		$login->attach( $this );
	}

	public function update( SplSubject $subject ) {
		if ( $this->login === $subject )
			$this->doUpdate( $subject );
	}
	abstract function doUpdate( Login $login );
}

class SecurityMonitor extends LoginObserver{
	public function doUpdate( Login $login ) {
		$status = $login->getStatus();
		if ( $status[0] == Login::LOGIN_WRONG_PASS )
			echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";
	}
}

$login = new Login();
new SecurityMonitor( $login );
$login->handleLogin( 'XXX', 'XXX', '127.0.0.1' );
?>

 

 

 

代码都写完了,还是要懂点理论的。

观察者模式的定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。观察者模式由四种角色构成:

1、Subject被观察者

定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责,管理观察者并通过观察者。

2、Observer观察者

观察者接收到消息后,即进行update操作,对接收到的信息进行处理。

3、ConcreteSubject具体的被观察者

定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

4、ConcreteObserver具体的观察者

每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

 

 

观察者模式的优点

1、观察者和被观察者之间是抽象耦合

如此设计,则不管是增加观察者还是被观察者都非常容易扩展,而且在java、php中都已经实现的抽象层级的定义,在系统扩展方面更是得心应手。

2、建立一套触发机制

根据单一职责原则,每个类的职责是单一的,那么怎么把各个单一的职责串联成真实世界的复杂的逻辑关系呢?观察者模式可以完美地实现这里的链条形式

 

 

 

观察者模式的缺点

观察者模式需要考虑一下开发效率和运行效率的问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在php中消息的通知是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般多考虑异步的方式。多级触发时的效率更是让人担忧,设计时注意。

 

 

 

观察者模式的使用场景

1、关联行为场景。需要注意的是,关系行为是可拆分的,而不是“组合”关系

2、事件多级触发场景

3、跨系统的消息交换场景,如消息队列的处理机制

 

  • 大小: 10.8 KB
  • 大小: 14.3 KB
2
0
分享到:
评论

相关推荐

    30拓展 9:隔墙有耳 —— Redis 安全通信(1).md

    30拓展 9:隔墙有耳 —— Redis 安全通信(1)

    鸡国大冒险运行程序,点开即用

    基于Python+Flask的安全多方计算的隐私保护系统设计与实现+全部资料齐全+部署文档.zip

    【资源说明】 基于Python+Flask的安全多方计算的隐私保护系统设计与实现+全部资料齐全+部署文档.zip基于Python+Flask的安全多方计算的隐私保护系统设计与实现+全部资料齐全+部署文档.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    聊天系统(java+applet).zip

    聊天系统(java+applet)

    setuptools-11.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip

    基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip基于UCI Heart Disease数据集的心脏病分析python源码+数据集+演示ppt+详细资料.zip

    setuptools-1.3.1-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    setuptools-40.3.0-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    公开整理-各省碳排放数据集(1990-2022年).xlsx

    详细介绍及样例数据:https://blog.csdn.net/li514006030/article/details/138872006

    基于Java的两个通用安全模块的设计与实现.zip

    基于Java的两个通用安全模块的设计与实现

    setuptools-14.1.1-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    华为-智慧园区数字平台技术qytp.pptx

    华为-智慧园区数字平台技术qytp.pptx

    30个Python游戏源码

    30个Python游戏源码

    基于python深度度量学习准确预测蛋白质二级结构源码.zip

    基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip基于python深度度量学习准确预测蛋白质二级结构源码.zip

    基于单片机的语音存储与重放系统.zip

    基于单片机的系统

    Java项目之jspm房屋租赁系统(源码 + 说明文档)

    Java项目之jspm房屋租赁系统(源码 + 说明文档) 2开发技术介绍 6 2.1B/S架构 6 2.2Java技术 6 2.3MySQL介绍 6 2.4MySQL环境配置 7 2.5JSP技术介绍 7 3系统分析 8 3.1可行性分析 8 3.1.1技术可行性 8 3.1.2经济可行性 8 3.1.3操作可行性 8 3.1.4运行可行性 9 3.2系统性能需求分析 9 3.3系统功能分析 9 3.4系统流程的分析 10 3.4.1用户管理的流程 11 3.4.2个人中心管理流程 12 3.4.3登录流程 12 4系统设计 13 4.1软件功能模块设计 13 4.2数据库设计与实现 13 4.2.1概念模型设计 13 4.2.2物理模型设计 15 5系统详细设计 22 5.1系统功能模块 22 5.2管理员功能模块 23 5.3租客功能模块 27 6系统测试 28 7总结与心得体会 29 7.1总结 29 7.2心得体会 29

    小红书-课程网盘链接提取码下载 (2).txt

    小红书-课程网盘链接提取码下载 (2).txt

    setuptools-24.1.1.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    c-master (2).zip

    c语言

    setuptools-19.4.1-py2.py3-none-any.whl

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

Global site tag (gtag.js) - Google Analytics