“relax_please”项目中观察者模式解析

“relax_please”项目中观察者模式解析

1 项目简介

  作者用python实现的一个项目,提醒程序员不要过度工作,使用观察者模式实现,调皮的作者把项目命名为:休息吧,程序员!

  代码链接如下:https://github.com/cooljacket/relax_please/blob/master/relax_please.py

2 观察者模式简介

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

3 观察者模式在该项目中的原理分析

  这个项目由以下两部分组成:

    1) 监听器:监听系统的键盘输入、系统时间。

    2) 发送提醒:一旦监听的事件发生(比如超过11点,持续工作超过1小时等),那么监听器就会触发“发送提醒器”,提醒器会以某种方式发送通知给使用者。

  这个项目的对象关系是一个很经典的“观察者模式”的模型,UML图如下:

  1). 监听器抽象接口Listener,它实现了所有监听器共有的功能,即绑定观察者,解绑观察者,(在所监听的事件发生的时候)通知所有绑定的观察者,以及监听事件。

class Listener:

    def __init__(self):

        self._observers = []

 

    def attach(self, observer):

        if not observer in self._observers:

            self._observers.append(observer)

 

    def detach(self, observer):

        try:

            self._observers.remove(observer)

        except ValueError, e:

            pass

 

    def notify(self):

        for observer in self._observers:

            observer.update()

 

    def listening(self):

        pass
View Code

   2) 需要监听两个事件,一个是使用电脑的情况(通过键盘输入来估计),一个是当前的工作时间(看看是否有熬夜),所以继承实现了两个具体的监听器,KeyBoardListener和SayGoodNightListener,它们都只需要实现listening函数即可,因为其它的都在抽象类Listener中实现好了!

class KeyBoardListener(Listener):

    """使用观察者模式来实现对键盘敲击的监听!"""

    format = "llHHI"

    size = struct.calcsize(format)

    keyboardDevice = "/dev/input/event3"

    fd = os.open(keyboardDevice, os.O_RDWR)

 

    def __init__(self, maxWorkTime=3600, leastRelaxTime=600, notifyInterval=60):

        Listener.__init__(self)

 

        self.maxWorkTime = maxWorkTime

        self.leastRelaxTime = leastRelaxTime

        self.notifyInterval = notifyInterval

 

        self.lastInputTime = time.time()

        self.lastNotifyTime = time.time() - notifyInterval

        self.start_work_time = time.time()

 

    def listening(self):

        while True:

            op = os.read(self.fd, self.size)

            # timestamp, subsecond, type, code, value = struct.unpack(format, op)

            now, _, _, _, _ = struct.unpack(self.format, op)

 

            # 如果中间有休息过了,那么更新开始工作的时间

            if now - self.lastInputTime >= self.leastRelaxTime:

                self.start_work_time = now

            elif now - self.start_work_time >= self.maxWorkTime:

                # 如果持续工作超过指定时间段,并且距离上次提醒的时间超过设定区间,就开始发提醒!

                if now - self.lastNotifyTime >= self.notifyInterval:

                    self.notify()

                    self.lastNotifyTime = now

            self.lastInputTime = now
View Code
class SayGoodNightListener(Listener):

    def __init__(self, night=23, morning=5, notifyInterval=30):

        Listener.__init__(self)

        self.morning = morning

        self.night = night

        self.notifyInterval = notifyInterval

 

    def listening(self):

        while True:

            now = time.localtime(time.time())

            if now.tm_hour >= self.night or now.tm_hour <= self.morning:

                self.notify()

            time.sleep(self.notifyInterval)
View Code

  3)观察者抽象类Observer,这个其实只是个接口而已,不是一个类,因为update()虽然是共有的接口函数,但它的实体是因人而异的,没法在这个类里给出一个default的实现(对比一下Listener的notify方法就知道了)。

class Observer:

    def update(self):

        raise NotImplementedError("Must subclass me")

  4)该项目,目前的发通知给使用者,只实现了一种方式:发送泡泡弹窗到桌面右上角,所以只写了一个NotifySendObserver类。

class NotifySendObserver(Observer):

    """通过ubuntu上的nofity-send在屏幕右上角发送提醒信息"""

 

    def __init__(self, title, msg):

        self.title = title

        self.msg = msg

 

    def update(self):

        self.sendNotification(self.title, self.msg)

 

    def sendNotification(self, title, msg):

        os.system('export DISPLAY=:0.0; notify-send "{0}" "{1}"'.format(title, msg))
View Code

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

4 观察者模式给该项目带来了哪些好处

  对于这个项目而言:观察者模式把监测事件和响应事件这两个职责给分离了,各司其职。监听器监测使用者有没有熬夜,有的话,就通过接口告诉观察者,观察者再发通知给使用者,叫ta赶紧睡觉。

  即观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。观察者模式在观察目标和观察者之间建立一个抽象的耦合。且观察者模式符合“开闭原则”的要求。

猜你喜欢

转载自www.cnblogs.com/zhouchengcheng/p/9832416.html