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章 老板来了——观察者模式的更多相关文章

  1. [Python设计模式] 第21章 计划生育——单例模式

    github地址:https://github.com/cheesezh/python_design_patterns 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式 ...

  2. [Python设计模式] 第16章 上班,干活,下班,加班——状态模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用代码模拟一天的工作状态,上午状态好,中午想睡觉,下午渐恢复,加班苦煎熬. ...

  3. [Python设计模式] 第1章 计算器——简单工厂模式

    github地址:https://github.com/cheesezh/python_design_patterns 写在前面的话 """ 读书的时候上过<设计模 ...

  4. [Python设计模式] 第24章 加薪审批——职责链模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下情景 员工向经理发起加薪申请,经理无权决定,需要向总监汇报, ...

  5. [Python设计模式] 第22章 手机型号&软件版本——桥接模式

    github地址:https://github.com/cheesezh/python_design_patterns 紧耦合程序演化 题目1 编程模拟以下情景,有一个N品牌手机,在上边玩一个小游戏. ...

  6. [Python设计模式] 第9章 如何准备多份简历——原型模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 设计一个简历类,必须有姓名,可以设置性别和年龄,即个人信息,可以设置曾就职 ...

  7. [Python设计模式] 第7章 找人帮忙追美眉——代理模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目1 Boy追求Girl,给Girl送鲜花,送巧克力,送洋娃娃. class ...

  8. [Python设计模式] 第28章 男人和女人——访问者模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下不同情况: 男人成功时,背后多半有一个伟大的女人: 女人成功 ...

  9. [Python设计模式] 第26章 千人千面,内在共享——享元模式

    github地址:https://github.com/cheesezh/python_design_patterns 背景 有6个客户想做产品展示网站,其中3个想做成天猫商城那样的"电商风 ...

随机推荐

  1. js获取元素提示信息

    js获取元素提示信息 var date=$("#date").attr('placeholder'); js修改元素的提示信息 $("#date").attr( ...

  2. 062 SparkStream内部原理

    1.DStream 内部是一系列的RDD组成的,每个RDD与RDD的产生时间形成一个pair保存在内存中(下面有) RDD包含了对应时间段的所有block数据. 2.DStream下的方法 /** T ...

  3. Go版GTK:环境搭建(windows)

    Go版GTK:环境搭建(windows) https://blog.csdn.net/tennysonsky/article/details/79221507 所属专栏: Go语言开发实战     1 ...

  4. selenium设置chrome和phantomjs的请求头信息

    selenium设置chrome和phantomjs的请求头信息   出于反爬虫也好-跳转到手机端页面也好都需要设置请求头,那么如何进行呢? 目录 一:selenium设置phantomjs请求头: ...

  5. Visual Assist X 助手下载

    链接:http://pan.baidu.com/s/1pLUErhT 密码:c6in

  6. BZOJ.1109.[POI2007]堆积木Klo(DP LIS)

    BZOJ 二维\(DP\)显然.尝试换成一维,令\(f[i]\)表示,强制把\(i\)放到\(a_i\)位置去,现在能匹配的最多数目. 那么\(f[i]=\max\{f[j]\}+1\),其中\(j& ...

  7. rest framework 源码流程

    1. def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the ...

  8. 如何使用单例模式返回一个安全的Connection

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public clas ...

  9. tableview分割线

    默认分割线,左边不到屏幕: TableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 三种结构体样式: /** UITab ...

  10. 3ds max学习笔记(五)--操作工具

    一些快捷:移动,旋转,缩放右键可调整数据或者直接右键点开也可以看到或者快捷键W,E,R 复制物体:摁住shift+移动工具,往想要复制的位置拖拽(实例方式复制)亦可选择多个父对象