[Python设计模式] 第14章 老板来了——观察者模式
github地址:https://github.com/cheesezh/python_design_patterns
题目
用程序模拟以下情景,在一个办公室里,当老板进门的时候,前台秘书就偷偷通知办公室里的同事:“老板来了”,办公室里的同事就会停止观看股票,继续工作。
基础版本
class Secretary():
"""
通知者
"""
def __init__(self):
self.observers = []
self.action = None
def attach(self, observer):
self.observers.append(observer)
def notify(self):
for observer in self.observers:
observer.update()
class Observers():
"""
观察者
"""
def __init__(self, name, informer):
self.name = name
self.informer = informer
def update(self):
print("{} {}关闭股票行情, 继续工作.".format(self.informer.action, self.name))
def main():
informer = Secretary()
hh = Observers("贺贺", informer)
mm = Observers("曼曼", informer)
informer.attach(hh)
informer.attach(mm)
informer.action = "老板来了!"
informer.notify()
main()
老板来了! 贺贺关闭股票行情, 继续工作.
老板来了! 曼曼关闭股票行情, 继续工作.
点评
- 上述代码基本能够表示设定的场景,但是有一个问题,“通知者”和“观察者”这两个类互相耦合,即双向耦合;
- 假设另一类“观察者”不看股票,而是在看NBA比赛,那么这类“观察者”对应的update操作就不应该是“关闭股票行情”,而是“关闭NBA直播”;
- 此外,除了前台秘书可以作为“通知者”,老板其实也是“通知者”,当老板来了,各个“观察者”也应该及时回到工作状态;
- 综上,需要对“观察者”和“通知者”进行进一步抽象
改进版本——双向解耦
from abc import ABCMeta,abstractmethod
class Informer():
"""
抽象通知者
"""
__metaclass__ = ABCMeta
@abstractmethod
def attach(self, observer):
pass
@abstractmethod
def detach(self, observer):
pass
@abstractmethod
def notify(self):
pass
class Boss(Informer):
"""
具体通知者
"""
def __init__(self):
self.observers = []
self.action = None
# 老板特有的初始化操作
def attach(self, observer):
print("聘用:{}".format(observer.name))
self.observers.append(observer)
def detach(self, observer):
print("解聘:{}".format(observer.name))
self.observers.remove(observer)
def notify(self):
print("--老板发出强大的气场--")
for o in self.observers:
o.update()
class Secretary(Informer):
"""
具体通知者
"""
def __init__(self):
self.observers = []
self.action = None
# 秘书特有的初始化操作
def attach(self, observer):
print("关系和谐:{}".format(observer.name))
self.observers.append(observer)
def detach(self, observer):
print("产生矛盾:{}".format(observer.name))
self.observers.remove(observer)
def notify(self):
print("--秘书发送即时消息--")
for o in self.observers:
o.update()
class Observer():
"""
抽象观察者
"""
__mataclass__ = ABCMeta
@abstractmethod
def __init__(self, name, informer): # 之所以要有informer,是为了在观察者内部访问到informer的对象
pass
@abstractmethod
def update(self):
pass
class StockObserver(Observer):
"""
具体观察者
"""
def __init__(self, name, informer):
self.name = name
self.informer = informer
def update(self):
print("{} {}关闭股票行情, 继续工作.".format(self.informer.action, self.name))
class NBAObserver(Observer):
"""
具体观察者
"""
def __init__(self, name, informer):
self.name = name
self.informer = informer
def update(self):
print("{} {}关闭NBA直播, 继续工作.".format(self.informer.action, self.name))
def main():
boss = Boss()
hh = StockObserver("贺贺", boss)
mm = NBAObserver("曼曼", boss)
boss.attach(hh)
boss.attach(mm)
boss.action = "我回来了!"
boss.notify()
secretary = Secretary()
mr = StockObserver("鸣人", secretary)
zz = NBAObserver("佐助", secretary)
xy = NBAObserver("小樱", secretary)
secretary.attach(mr)
secretary.attach(zz)
secretary.attach(xy)
secretary.detach(xy)
secretary.action = "老板回来了!"
secretary.notify()
main()
聘用:贺贺
聘用:曼曼
--老板发出强大的气场--
我回来了! 贺贺关闭股票行情, 继续工作.
我回来了! 曼曼关闭NBA直播, 继续工作.
关系和谐:鸣人
关系和谐:佐助
关系和谐:小樱
产生矛盾:小樱
--秘书发送即时消息--
老板回来了! 鸣人关闭股票行情, 继续工作.
老板回来了! 佐助关闭NBA直播, 继续工作.
观察者模式(发布-订阅模式)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
Observer类,即抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个update方法。
ConcreteSubject类,叫做具体主题或者具体通知者,将有关状态存入具体观察者对象。在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
ConcreteObserver类,即具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如有需要,具体观察者角色可以保存一个指向具体主题对象的引用,以便获取具体主题对象的状态。
观察者模式特点
讲一个系统分割成一系列相互写作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护,扩展和重用都带来不便[DP]。而观察者的关键对象使主题Subject和观察者Observer,一个主题可以有任意树木的依赖它的Observer,一旦Subject状态发生变化,所有的Observer都可以得到通知。
那么什么时候使用观察者模式呢?
当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,需要考虑使用观察者模式。另外,当一个抽象模型有两个方面,其中一个方面依赖另一个方面,这时候使用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
总地来将,观察者模式所做的工作其实就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体实现。从而使得各自的变化不会影响另一边的变化。
[Python设计模式] 第14章 老板来了——观察者模式的更多相关文章
- [Python设计模式] 第21章 计划生育——单例模式
github地址:https://github.com/cheesezh/python_design_patterns 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式 ...
- [Python设计模式] 第16章 上班,干活,下班,加班——状态模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用代码模拟一天的工作状态,上午状态好,中午想睡觉,下午渐恢复,加班苦煎熬. ...
- [Python设计模式] 第1章 计算器——简单工厂模式
github地址:https://github.com/cheesezh/python_design_patterns 写在前面的话 """ 读书的时候上过<设计模 ...
- [Python设计模式] 第24章 加薪审批——职责链模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下情景 员工向经理发起加薪申请,经理无权决定,需要向总监汇报, ...
- [Python设计模式] 第22章 手机型号&软件版本——桥接模式
github地址:https://github.com/cheesezh/python_design_patterns 紧耦合程序演化 题目1 编程模拟以下情景,有一个N品牌手机,在上边玩一个小游戏. ...
- [Python设计模式] 第9章 如何准备多份简历——原型模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 设计一个简历类,必须有姓名,可以设置性别和年龄,即个人信息,可以设置曾就职 ...
- [Python设计模式] 第7章 找人帮忙追美眉——代理模式
github地址:https://github.com/cheesezh/python_design_patterns 题目1 Boy追求Girl,给Girl送鲜花,送巧克力,送洋娃娃. class ...
- [Python设计模式] 第28章 男人和女人——访问者模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下不同情况: 男人成功时,背后多半有一个伟大的女人: 女人成功 ...
- [Python设计模式] 第26章 千人千面,内在共享——享元模式
github地址:https://github.com/cheesezh/python_design_patterns 背景 有6个客户想做产品展示网站,其中3个想做成天猫商城那样的"电商风 ...
随机推荐
- c#生成连续单号
bill.BillCode = GetBillCode("JH");//生成单号 if (bill.BillCode == "no") { bill.BillC ...
- CF552 E. Two Teams
题意:给出一串n个数 为1-n的乱序 一共有两个教练 教练一的队伍是1队 二是二队 教练一选择 当前队列中剩余人数的最大序号 将其和左边k个人 和右边k个人 变为一队 如此反复直到所有人 ...
- 070 DStream中的transform和foreachRDD函数
1.说明 DStream的API不够满足使用的时候,可以使用这两个函数,将dstream转换为rdd,然后进行操作 2.transform transform:将DStream的操作转换为RDD的操作 ...
- springboot拦截器中获取配置文件值
package com.zhx.web.interceptor; import com.zhx.util.other.IpUtil; import org.slf4j.Logger; import o ...
- JAVA项目中常用的异常处理情况
1.数学运算异常( java.lang.arithmeticexception) 程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不 ...
- UVa 11987 Almost Union-Find (虚拟点)【并查集】
<题目链接> 题目大意: 刚开始,1到n个集合中分别对应着1~n这些元素,然后对这些集合进行三种操作: 输入 1 a b 把a,b所在的集合合并 输入 2 a b 把b从b所在的旧集合移到 ...
- BZOJ.4771.七彩树(可持久化线段树)
BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...
- Shell脚本笔记(五)Shell函数
Shell函数 1.定义语法 标准写法: funciton funName () { order....... return n } 简化写法1: funciton funName { order.. ...
- Max Factor 2710 最大的合数的质数因子
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2710 思路:用类似“埃氏筛法”求素数的方法 只是不在把合数标记为1 而是标记为他是因子数. 最后比较大小即 ...
- 潭州课堂25班:Ph201805201 django框架 第三课 模板路径,变量,过滤器,静态文件的引用 (课堂笔记)
第二种方法 首先 'APP_DIRS': True, 将 app 的名字在 INSTALLED_APPS 进行注册 模板变量 传的各种数据类型,和取值 过渡器: 静态文件: 在项目文件目录 中创建 ...