本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题:

  1. 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的问题?
  2. 是什么?通过Python语言来去实现这个设计模式,用于解决为什么中提到的问题。
  3. 怎么用?理解了为什么我们也就基本了解了什么情况下使用这个模式,不过在这里还是会细化使用场景,阐述模式的局限和优缺点。

一些基本概念

在开始讲本次设计模式之前,我们先搞清楚软件设计中的一些基本概念吧。软件设计中我们有一个常用的概念叫“耦合”,我们常常说模块之间要“解耦合”,软件设计要“松耦合”。那么什么是“耦合”呢?
实际上耦合是指两个模块之间(模块可以理解为两坨代码,所以可以是函数,可以是类,或者更大范围的系统)发生了关系,比如一个模块调用了另一个模块的函数,或者一个模块需要获取另一个模块的数据,总之有了联系就有了耦合。所谓“解耦合”或者“松耦合”是说让两个模块之间的联系没有那么紧密,这样一个模块的变化不会影响另一个模块的代码。那么我们如何来使我们的系统是一个“松耦合”的系统呢?
实现“松耦合”最重要的就是抽象一致性,白话就是要通过抽象的接口(Python中没有接口的概念,可以理解为抽象的类)来联系调用方和被调方。
一般的,假设我们实现了两个类A和B,我们可能会如下调用

 
classcall.jpg

代码可能是这样的

class A(object):
def __init__(self):
self.obj = B(param1, param2) def method_of_a(self):
self.obj.method_of_b(param3) class B(object):
def __init__(self, param1, param2):
... def method_of_b(self, param3):
# do something
....

但是这种情况下,如果B发生改变,或者想换成另一个类C,这时就需要更改A的代码,那么如果我们把A和B之间的联系抽象出来,通过接口(或者类似接口的东西)来连接A和B我们就可以某种程度上屏蔽这种变化,如下图所示:

 
interface.jpg

可能的代码如下:

class A(object):
def __init__(self, some_obj):
self.obj = some_obj def method_of_a(self):
self.obj.consistent_method(param3) class B(object):
def __init__(self, param1, param2):
...
def consistent_method(self, param3):
... class C(object):
def __init__(self, param1, param2):
...
def consistent_method(self, param3):
...

可以看出由于Python对参数没有类型约束,所以天然的支持接口,这也是Python灵活的地方之一。在这个例子中,我们的参数some_obj可以想象成一个抽象类或者Java中的接口,我们传递参数时可以传递具体实现类的对象(比如B或者C的对象),但在这里只是一个抽象

另外,我们的B和C都实现了consistent_method,这就是一致性的体现。

为什么

假设我们设计了某种功能,当用户点击页面上的按钮时,我们需要为用户做两件事,一是做页面变换显示出预期的效果,二是响起特定的音乐。那么我们的代码可能是这样的:

def onClick():
changePage()
playMusic()

后来你的需求有变动,还需要记录用户点击时间,需要弹出提示信息,需要......
于是你需要不断的修改上述的代码,可能是这样的

def onClick():
changePage()
playMusic()
recordTime()
popupHint()
...

我们认为onClick()和这些功能函数耦合的过于紧密了,每次的改动都会影响onClick函数。这类问题抽象出来就是当一个事件发生时需要调用很多功能模块,而解决这类问题最好的方式就是观察者模式,也叫发布-订阅模式。

是什么

观察者模式是说你有一个观察者列表,这个列表中的函数或者某种功能都在观察某个事件的发生,一旦发生,这些函数或者功能就会自动执行,这个其实很好理解,如下图:

 
publisher_subscriber.jpg

我们还是上代码:

class Button(object):
"""publisher or subject"""
def __init__(self):
self.observer_list = [] def register(self, func):
self.observer_list.append(func) def unregister(self, func):
self.observer_list.remove(func) def onClick():
for func in self.observer_list:
func()
... def playMusic():
"""subscriber or observer"""
... def changePage():
"""subscriber or observer"""
...
... def main():
button = Button()
button.register(playMusic)
button.register(changePage)
...

通过这种方式,我们实现了发布者和订阅者之间的松耦合,它们之间并不直接联系,而是通过统一的register/unregister来绑定和解绑定。

怎么用

通过上面的代码,细心的同学可能会观察到似乎代码也没少啊,而且注册的时候不还是要修改代码去注册新的功能吗?能想到这的同学,你的批判性思维很好,鼓励一下!
这里基于以下几种好处我们要使用这个模式:

  • 使用这个模式的最大好处之一就是灵活
    我们可以动态的修改监听的事件,比如用户不想在点击该按钮的时候响起音乐,那么当ta不选择这一项时,我们的程序可以灵活的通过unregister来解绑定,试想如果你不使用观察者模式,是很难解绑定的,你就需要去修改代码,显然这不现实。
# when user uncheck play music option
button.unregister(playMusic)

所以,使用这个模式的理由之一就是你需要动态的绑定和解绑相应的功能时,你就需要观察者模式。

  • 第二个好处是代码复用
    比如你有很多个按钮,都需要统一的注册某些功能,这个时候你就可以实现一个父类,在初始化的时候将所有的需要注册的功能都注册好,子类直接继承就好了,子类当然还可以注册自己特殊功能。可能的代码如下:
class Button(object):
def __init__(self):
self.observer_list = []
self.observer_list.register(playMusic)
self.observer_list.register(popupHint) def register(self, func):
self.observer_list.append(func) def unregister(self, func):
self.observer_list.remove(func) def onClick():
for func in self.observer_list:
func() class ButtonA(Button):
def __init__(self):
super(ButtonA, self).__init__()
... class ButtonB(Button):
def __init__(self):
super(ButtonA, self).__init__()
self.observer_list.register(changePage)
...

好了,观察者模式到这里你就应该很清楚了,如果你觉得有收获,不妨点个赞,如果你觉得非常赞,那就打个赏,鼓励是一种美德!

设计模式(Python)-观察者模式的更多相关文章

  1. 大话设计模式Python实现-观察者模式

    观察者模式(发布-订阅模式 Publish Subscribe Pattern):定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,当主题对象状态发生变化时会通知所有观察者,是它们能够自动更 ...

  2. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  3. 设计模式之观察者模式(Observable与Observer)

    设计模式之观察者模式(Observable与Observer) 好久没有写博客啦,之前看完了<设计模式之禅>也没有总结一下,现在回忆一下设计模式之观察者模式. 1.什么是观察者模式 简单情 ...

  4. 8.5 GOF设计模式四: 观察者模式Observer

    GOF设计模式四: 观察者模式Observer  现实中遇到的问题  当有许多不同的客户都对同一数据源感兴趣,对相同的数据有不同的处理方式,该如 何解决?5.1 定义: 观察者模式  观察者模式 ...

  5. php 设计模式之观察者模式(订阅者模式)

    php 设计模式之观察者模式 实例 没用设计模式的代码,这样的代码要是把最上面那部分也要符合要求加进来,就要修改代码,不符合宁增不改的原则 介绍 观察者模式定义对象的一对多依赖,这样一来,当一个对象改 ...

  6. [JS设计模式]:观察者模式(即发布-订阅者模式)(4)

    简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...

  7. 实践GoF的23种设计模式:观察者模式

    摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...

  8. [python实现设计模式]-4.观察者模式-吃食啦!

    观察者模式是一个非常重要的设计模式. 我们先从一个故事引入. 工作日的每天5点左右,大燕同学都会给大家订饭. 然后7点左右,饭来了. 于是燕哥大吼一声,“饭来啦!”,5点钟定过饭的同学就会纷纷涌入餐厅 ...

  9. python 设计模式之观察者模式

    观察者模式是一个软件设计模式,一个主题对象博包涵一系列依赖他的观察者,自动通知观察者的主题对象的改变,通常会调用每个观察者的一个方法.这个设计模式非常适用于分布式事件处理系统. 典型的在观察者模式下: ...

随机推荐

  1. uva10537 dijkstra + 逆推

    21:49:45 2015-03-09 传送 http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8& ...

  2. ASP.NET之报表--RDLC(一)---附源码

    听同事介绍到RDLC,之前有了解过报表,但是确实没什么放在心上.最近有空,就研究下了. 一.RDLC实现 1.步骤 (1)首先新建一个项目RDLCDemo (2)新建一个DataSet数据集,并且绑定 ...

  3. ng-深度学习-课程笔记-6: 建立你的机器学习应用(Week1)

    1 训练/验证/测试集( Train/Dev/test sets ) 构建神经网络的时候有些参数需要选择,比如层数,单元数,学习率,激活函数.这些参数可以通过在验证集上的表现好坏来进行选择. 前几年机 ...

  4. myeclipse安装jadclipse(反编译工具)

    我是myeclipse5. 的IDE工具.为了能反编译class文件,上网搜索了很多资料,终于找到一下的一段资料: .将jad.exe 复制到myeclipse安装目录的jre/bin目录下, 如:C ...

  5. Underscore-逐行分析

    标签: // Underscore.js 1.8.3// http://underscorejs.org// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud ...

  6. 前端学习笔记之CSS网页布局

    CSS网页布局   阅读目录 一 网页布局方式 二 标准流 三 浮动流 四 定位流 一 网页布局方式 #1.什么是网页布局方式 布局可以理解为排版,我们所熟知的文本编辑类工具都有自己的排版方式,比如w ...

  7. Maven聚合项目在eclipse中显示没有层次

    大部分时间都在用idea做maven的项目,今天用eclipse导入了maven项目,果然不出所料,界面有显示问题,各个模块都堆叠在同一层级,根本看不出父项目与子项目之间的层次关系,见下图: 于是找修 ...

  8. JS中的slice和splice

    1,slice  : 定义:接收一个或两个参数,它可以创建一个由当前数组中的一项或多项组成的新数组,注意是新数组哦~ 也就是说它不会修改原来数组的值. 用法:slice( para1 ),会截取从pa ...

  9. What's the difference between SDK and Runtime in .NET Core?

    What's the difference between SDK and Runtime in .NET Core? Answer1 According to the .Net Core Guide ...

  10. 使用 p4-graphs 命令将p4程序依赖关系图形化

    位置:/home/wasdns/p4factory/targets/l2_switch/p4src 命令: cd /home/wasdns/p4factory/targets/l2_switch/p4 ...