[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个想做成天猫商城那样的"电商风 ...
随机推荐
- python面试题之Python是如何进行内存管理的
python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收. ...
- day19 正则,re模块
http://www.cnblogs.com/Eva-J/articles/7228075.html 所有常用模块的用法 正则的规则: 在一个字符组里面枚举合法的所有字符,字符组里面的任意一个字符和 ...
- 基本标签-html-1
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 正则表达式在python中的简单使用
正则表达式独立与编程语言,基本上所有的编程语言都实现了正则表达式的相关操作.在Python中正则表达式的表现为re模块: import re 其操作有三个方法: my_string = "h ...
- Dev-C++安装第三方库boost
Dev-C++安装第三方库boost 转 https://www.jianshu.com/p/111571e4d6f5?utm_source=oschina-app 之前鉴于codeblocks界面 ...
- 浅谈云计算SPI(SaaS、PaaS、IaaS)
The other day, I arrived at the SAP LABS CHINA for interview with my pleasure. That gave me a chance ...
- LanProxy 内网映射穿透
前言:用过 ngrok 的人都知道,这是一个免费并且简便的内网映射工具,可是现在ngrok不知道弄啥?不能用了,那我们只能去找一些新的工具,下面是我跟我朋友一起弄的(主要是他教我(✪ω✪)),免费的, ...
- Java笔记(十四) 并发基础知识
并发基础知识 一.线程的基本概念 线程表示一条单独的执行流,它有自己的程序计数器,有自己的栈. 1.创建线程 1)继承Thread Java中java.lang.Thread这个类表示线程,一个类可以 ...
- mac中安装 RabbitMQ
1.brew install rabbitmq 2.安装后,进入/usr/local/Cellar/rabbitmq/3.7.7 ,输入:sbin/rabbitmq-server 出现下面日志,说明启 ...
- python 文件指针及文件覆盖
1.文件纯净模式延伸 r+t:可读.可写 w+t:可写.可读with open('b.txt','w+t',encoding='utf-8') as f: print(f.readable()) pr ...