首页 >

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

后端开发|php教程隔墙有耳的观察者模式(Observer Patern)
php, 设计模式, 观察者模式, SPL 隔墙耳的观察者模式(Observer Patern)
后端开发-php教程
登录系统想必大家都做过,验证用户名密码就登录成功,日志系统应该记录此次登录,如果登录出错,安全系统应该会记录此次错误,邮件系统也应该会发送相关邮件给管理员,等等。这就好像登录系统被很多人监视一样,一旦有什么风吹草动,立即会被其它系统获悉。那就用观察者模式来试试,类图如下:
一元云购手机网站源码,ubuntu 默认su密码,爬虫打印网页内容,zqf php,seo 空标签lzw
隔墙有耳的观察者模式(Observer Patern)
很简单的模式,实现代码:
ar app源码,vscode豆沙绿,linux基础入门ubuntu,tomcat口令周期,sqlite创建时间,易语言大漠插件教程,vue前端自适应框架,r 网络爬虫,php 的时间戳,SEO入门书籍设计,it企业网站源码免费下载,手机网页 欣赏,齐博模板制作教程,css设置页面宽度自适应屏幕大小,教师档案管理系统登录界面代码,微擎 微赞程序根目录lzw
Php代码
apple id 源码,服务器如何配置vscode,进入ubuntu用户,tomcat管理页面解释,爬虫程序危害,php 有那些技术,南海公司seo优化怎么做lzw
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类负责保证它们的主体是正确的类型。它们甚至能将自己添加到主体上。类图如下:

隔墙有耳的观察者模式(Observer Patern)
实现代码如下:

Php代码

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代码

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、跨系统的消息交换场景,如消息队列的处理机制


隔墙有耳的观察者模式(Observer Patern)
  • JavaScript设计模式之建造者模式介绍【javascript】
  • JavaScript设计模式之建造者模式介绍【javascript】 | JavaScript设计模式之建造者模式介绍【javascript】 ...

    隔墙有耳的观察者模式(Observer Patern)
  • java设计模式中单例模式讲解
  • java设计模式中单例模式讲解 | java设计模式中单例模式讲解 ...

    隔墙有耳的观察者模式(Observer Patern)
  • php设计模式 Command(命令模式)
  • php设计模式 Command(命令模式) | php设计模式 Command(命令模式) ...