最近写了一个“休息吧,程序员”的小工具,用到了观察者模式,感觉自己的理解还不够深入,借此机会稍微深入学习一下。

项目介绍:提醒程序员不要过度工作的一个脚本,暖心程序跟你道晚安,使用观察者模式实现,我把它叫做:休息吧,程序员!

po图

项目地址:https://github.com/cooljacket/relax_please
先po一下我实现出来的效果图(注意桌面右上角):

暖心跟你道晚安~


如老妈一般的监督你不要过劳!

项目设计

首先说下这个项目的组成:
1. 监听器:监听系统的键盘输入、系统时间
2. 发送提醒:一旦监听的事件发生(比如超过11点,该睡觉了),那么监听器就会触发这个“发送提醒器”,提醒器会以某种方式发送通知给使用者

这样看来,这个项目的对象关系是一个很经典的“观察者模式”的模型,下面先看一下我画的UML图:

看不懂UML图的朋友也没关系,稍微解释一下就知道了:
1. 监听器抽象接口Listener,它实现了所有监听器共有的功能,即绑定观察者,解绑观察者,(在所监听的事件发生的时候)通知所有绑定的观察者,以及监听事件。
2. 我需要监听两个事件,一个是使用电脑的情况(通过键盘输入来估计),一个是当前的工作时间(看看是否有熬夜),所以继承实现了两个具体的监听器,KeyBoardListener和SayGoodNightListener,它们都只需要实现listening函数即可,因为其它的都在抽象类Listener中实现好了!
3. 观察者抽象类Observer,这个其实只是个接口而已,不是一个类,因为update()虽然是共有的接口函数,但它的实体是因人而异的,没法在这个类里给出一个default的实现(对比一下Listener的notify方法就知道了)。
4. 目前的发通知给使用者,只实现了一种方式:发送泡泡弹窗到桌面右上角,所以只写了一个NotifySendObserver类。

值得注意的是:一个监听器可以有多个观察者,而一般一个观察者只能有一个监听器。比如监听是否熬夜这个事件,可以有多种通知方式,比如响闹铃,发送泡泡弹窗到桌面的右上角,记录熬夜情况到日志文件以备后续分析,等等。而一般一个观察者都是特化了的,不会用在多个监听器上面,比如你熬夜的时候响一段“睡吧睡吧我亲爱滴宝贝……”的摇篮曲,是针对特定的这个(熬夜)事件的!

什么是观察者模式?

引用一段话1来描述:

建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。

注意这里,观察者并不是真的去观察,而是被观察目标通知才会得知有事件发生的!

这是一个事件驱动模型,不直接想它的好处,先反过来想,如果不这样做,而是让NotifySendObserver主动去询问/检测事件有无发生,这样的话,得设置好去检测的时间间隔,除非不停去检测,否则肯定是有概率会错过一些事件的。忙死忙活,吃力不讨好^_^(手动微笑)。

而观察者模式正是把检测事件和响应事件这两个职责给分离了,各司其职。我这边自己监视使用者有没有熬夜,有的话,我就通过接口告诉你,你再发通知给使用者,叫ta赶紧睡觉。没有发通知给你,你也不用来问我,就是没事情发生,各自清闲~

再举个简单的例子,就是我们平时按手机app里的按钮,那个应用内部肯定不会时刻去检测这个按钮有没有被按下,而是按钮被按下的时候,系统会发送一个消息给应用去处理。

Why抽象接口?

有人可能会说,哎,你怎么要搞这么麻烦啊,无论是监听器,还是观察者,都要抽象出一个接口出来?

这里安利一个我自己到现在还没懂透的“面向对象五大原则SOLID”:

参考维基百科:https://zh.wikipedia.org/wiki/SOLID_(%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1)

这里很明显运用的就是D,依赖反转原则。

问题还是回来了,用了有什么好处呢?不用,又有什么坏处呢?

这里推荐一篇关于依赖倒转原则的好文章,里边的“妈妈讲故事”的例子很鲜明地体现了这个原则的好处:高层模块可以自由选择底层模块,耦合性很低。

说人话,就是,在这个例子中,如果我不抽象一个Observer接口的话,那么Listener(高层模块)就直接依赖NotifySendObserver(底层模块)了,那如果我后续不想用这种通知方式,而是想改用音乐作为闹铃,是不是除了写多一个MusicObserver类,还要改动Listener类呢?

有人可能会说,这在python里都一样,因为每次运行都是重新编译,那试问一下,如果你是公开发布的一个东西,还能这么做吗?如果不是用python而是C++、Java之类的静态语言呢?

嗯,抽象接口的好处就是这样了。

而Listener作为一个抽象类,则不是同样的想法了。我想实现多个监听器类,它们有很鲜明的公共逻辑,比如上面的KeyBoardListener和SayGoodNightListener,那这个时候主要是继承重用的想法,而不是为了要依赖反转。


  1. http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html ↩

观察者模式与依赖反转原则相关推荐

  1. 【简译】关于依赖反转原则、控制反转和依赖注入的抽象的初学者指南

    原文在此. ======================================分割线==================================== 介绍 文章以介绍依赖反转原则开始 ...

  2. 依赖反转原则DIP 与使用了Repository模式的asp.net core项目结构

    DIP 依赖反转原则 Dependency Inversion Principle 的定义如下: 高级别的模块不应该依赖于低级别的模块, 他们都应该依赖于抽象. 假设Controller依赖于Repo ...

  3. 依赖反转原则(DIP)

    依赖反转原则.依赖反转原则的英文翻译是 Dependency Inversion Principle,缩写为 DIP.中文翻译有时候也叫依赖倒置原则. 为了追本溯源,我先给出这条原则最原汁原味的英文描 ...

  4. DIP依赖反转原则——实现松耦合的设计

    在<IoC控制反转设计原则--实现松耦合>我们将控制工作委派给其他类来完成,向松耦合设计又迈进了一步.但是我们类仍然依赖着具体的类,所以我们可以使用DIP(依赖反转原则)来进一步获得松耦合 ...

  5. 地表最强22个互联网定律和原则:炒作周期、依赖反转原则、Spotify模型……

    ▲ 点击上方"老于的笔记"关注公众号 回复1,免费获取B端运营地图 正文来了 精选了7个原则和15个定律,互联网人公认的牛出天际! 产品经理照着做策划,保证怼开发.怼测试.怼老板, ...

  6. 依赖反转(倒置)原则(Dependency inversion principle,DIP)

    在软件设计.编码过程中有几个基本原则即SOLID原则,学习理解能够帮忙我们写出更健壮的代码.SOLID是五个基本原则的首字母.这五个原则如下: Single responsibility Open–c ...

  7. 单一原则,开放-封闭原则,依赖反转,里氏族替换原则

    读书笔记:大话设计模式 单一职责原则 单一职责原则(SRP):就一个类而言,应该仅有一个引起它变化的原因 例子 一个俄罗斯方块游戏设计思路 其中游戏业务逻辑: 数据的每一项值变化的问题,下落,旋转,碰 ...

  8. 13. 设计模式之反转原则:如何减少代码间的相互影响?

    一.DIP:统一代码交互的标准 在上一讲中,我们学习了 SOLID 五大设计原则,了解了在实际工作中应该如何有针对性地使用这些原则.不过,其中有一个原则,可能是你用得很熟练,但是说到概念却又容易跟其他 ...

  9. Java依赖于抽象不依赖于具体,依赖倒置原则(Dependecy-Inversion Principle)

    依赖倒置原则(Dependence Inversion Principle,DIP)的原始定义: 高层模块不应该依赖底层模块,两者都应该依赖其抽象: 抽象不应该依赖细节: 细节应该依赖抽象. 抽象:即 ...

最新文章

  1. 雷林鹏分享:jQuery EasyUI 数据网格 - 创建属性网格
  2. makefile ifneq多个判断条件_一文入门Makefile
  3. 415. Add Strings
  4. 如何基于Spark进行用户画像?
  5. php datetime 对象,PHP DateTime 对象和 Date 函数的 Demo
  6. color 的一些处理
  7. php oci8 11,Linux下PHP5.2 Oracle客户端扩展(OCI8)安装
  8. pyqt5 列表内添加按钮
  9. Oracle开源Fn,加入Serverless之争
  10. paypal 支付接口 php,PHP整合PayPal支付
  11. linux 强制结束任务管理器,结束拒绝访问的进程 cmd下结束进程 强行结束进程
  12. app下载 微信扫码打开 提示用户用浏览器打开
  13. itest听力答案2020_大学英语itest2018答案
  14. 计算机网络八大性能指标
  15. java emoji表情_java处理emoji表情的方法
  16. 《Redis设计与实现》学习笔记
  17. k8s 安装nfs_kubernetes挂载nfs报错 | 运维笔记
  18. 微信分销商城如何保持客户粘性
  19. 教你使用 Python 获取美国重要经济指标数据
  20. ChatGPT一路狂飙,对于教培机构是危险还是机遇?

热门文章

  1. (附源码)springboot仓库管理系统的开发毕业设计260931
  2. 短信接口调用总结(个人学习版)
  3. 教你快速识别手机质量的好坏
  4. 强烈推荐:2018年互联网人最值得一读的书单
  5. linux性能(一):cpu性能指标及工具
  6. vue插件vue-particles,粒子动画特效背景,收藏起来,避免找不到!!!
  7. RS-485总线电平异常解决方案解析
  8. 剑指offer Q10 矩形覆盖
  9. Windows下Docker配置安装加速器
  10. 有关于as3的puremvc框架实现和理解