设计模式 | 观察者模式
说明
这个设计模式很常用、重要, 所以要好好看。这是一个非常经典的设计模式, 解决了不少问题, 在实际项目中应用比较广泛。
先简单说一下设计模式的定义: 一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。观察者模式实现了低耦合,非侵入式的通知与更新机制。
举个?, 大家都知道, 今年的猪肉价格让人有望而却步, 让原本就不富裕的家庭更是雪上加霜, 我有个想法就是, 让这个超市猪肉价格一降价就通知我, 好让我能及时以较低价格购买。 我这个时候我就需要观察者, 来观察猪肉的价格, 然后及时通知我。
示例
<?php
/**
* Created by 憧憬.
*/
/**
* 观察者约束
* Interface Observer
*/
interface Observer {
// 到时候接受到通知及时作出反应
public function update();
}
/**
* 被观察者 到时候也好通知观察者
* Interface Observable
*/
interface Observable {
// 添加观察者
public function addObserver(Observer $observer);
// 删除观察者
public function deleteObserver(Observer $observer);
// 通知观察者
public function notifyObserver();
}
//
///**
// * 猪肉
// * Class Pork
// */
//class Pork implements Observable {
//
// public $observableList;
//
// public $price;
//
// public function addObserver(Observer $observer)
// {
// $this->observableList[] = $observer;
// }
//
// public function deleteObserver(Observer $observer)
// {
// foreach ($this->observableList as $k => $v) {
// if ($v === $observer) {
// unset($this->observableList[$k]);
// }
// }
// }
//
// public function notifyObserver()
// {
// foreach ($this->observableList as $observer) {
// $observer->update();
// }
// }
//
// /**
// * 设置猪肉价格
// * @param $price
// * @author: 憧憬
// */
// public function setPrice($price)
// {
// $this->price = $price;
// $this->notifyObserver();
// }
//
//}
//
/**
* 博主自己
* Class Aoppp
*/
class Aoppp implements Observer {
public function update()
{
echo '憧憬 有你的通知来了 快快行动';
}
}
/**
* 李四也想吃猪肉
* Class Lisi
*/
class Lisi implements Observer {
public function update()
{
echo '李四 有你的通知来了 快快行动';
}
}
//$pork = new Pork();
//
//$pork->addObserver(new Aoppp());
//$pork->addObserver(new Lisi());
//$pork->setPrice(100);
/**
* 这个时候你发现 可以随便通知任何人, 也可以移除观察者 但是每个想观察的对象都要写这个函数 有点重复
* 我们可以抽离出来
*/
class ObserverUtil implements Observable {
public $observableList;
public function addObserver(Observer $observer)
{
$this->observableList[] = $observer;
}
public function deleteObserver(Observer $observer)
{
foreach ($this->observableList as $k => $v) {
if ($v === $observer) {
unset($this->observableList[$k]);
}
}
}
public function notifyObserver()
{
foreach ($this->observableList as $observer) {
$observer->update();
}
}
}
/**
* 将猪肉类改为继承
* Class Pork
*/
class Pork extends ObserverUtil {
public $price;
/**
* 设置猪肉价格
* @param $price
* @author: 憧憬
*/
public function setPrice($price)
{
$this->price = $price;
$this->notifyObserver();
}
}
$pork = new Pork();
$pork->addObserver(new Aoppp());
$pork->addObserver(new Lisi());
$pork->setPrice(100);
// 这样的话 你要监听鸡肉 还是什么乱七八糟的 都要方便很多了
观察者模式在实际项目的应用中非常常见,比如你到 ATM
机器上取钱,多次输错密码,卡就会被 ATM
吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生; 第三,初始化 ATM
机屏幕,返回最初状态,你不能因为就吞了一张卡,整个 ATM
都不能用了吧,一般前两 个动作都是通过观察者模式来完成的。
这个设计模式之后变种了一个发布/订阅
的模式, 这个设计模式也是比较容易理解, 大家可以去了解下。
那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是 观察者模式非常重要的功能。使用观察者模式也有两个重点问题要解决:
- 广播链的问题
如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了 一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器...,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双 重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消 息最多转发一次(传递两次),这还是比较好控制的;
- 异步处理问题
被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个 大家有时间看看 Message Queue,就会有更深的了解。
本文为作者原创,手码不易,允许转载,转载后请以链接形式说明文章出处。
- MongoDB中各种类型的索引
- 敲一天代码了,轻松下吧,精彩 !看人潮如海
- MongoDB副本集其他细节
- 数据库表设计对性能的影响
- V部落博客管理平台开源啦! Vue+SpringBoot强强联合!
- SpringBoot+SpringSecurity处理Ajax登录请求
- MongoDB副本集配置
- MongoDB副本集搭建
- vhr部门管理数据库设计与编程
- SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题(六)
- 权限管理模块中动态加载Vue组件
- axios请求封装和异常统一处理
- SpringSecurity中密码加盐与SpringBoot中异常统一处理
- SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题(二)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 图像处理笔记(3)----OpenCV图像算术运算
- V - Distpicker 是一个简单易用的地区选择器
- [漫谈] 状态
- 变量覆盖
- 【Rust日报】2020-08-27 在Arduino Uno上面跑 Rust 程序
- 浅谈类加载
- Rust FFI 编程 - Rust导出共享库06
- go语言文件操作汇总
- Redis 主从复制 哨兵模式实战
- 【Rust日报】2020-08-28 Rust 1.46稳定版发布
- go语言反射
- 和同事谈谈Flood Fill 算法
- 【每周一库】 img_hash,rust下的pHash算法库
- 【Rust日报】2020-08-29 生产环境 Rust 序列化库的选择
- 【投稿】刀哥:Rust学习笔记 5