作业三-Water项目中的观察者模式
Water项目中观察者模式解析
1 项目简介
用python实现的一个项目,提醒大家节约用水,不要随意浪费:当水龙头连续使用超过十分钟就提示使用者是否忘记关水了。
2 观察者模式简介
建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
3 观察者模式在该项目中的原理分析
3.1这个项目由以下两部分组成:
3.1.1监听器:监听水龙头打开时间。
3.1.2发送提醒:一旦监听的事件发生(比如连续打开水龙头超过五分钟),那么监听器就会触发“发送提醒器”,提醒器会以某种方式发送通知给使用者。
这个项目的对象关系是一个很经典的“观察者模式”的模型,UML图如下:
3.2.1监听器抽象接口Listener,它实现了所有监听器共有的功能,即绑定观察者,解绑观察者,(在所监听的事件发生的时候)通知所有绑定的观察者,以及监听事件。
1 class Listener:
2
3 def __init__(self):
4
5 self._observers = []
6
7
8
9 def attach(self, observer):
10
11 if not observer in self._observers:
12
13 self._observers.append(observer)
14
15
16
17 def detach(self, observer):
18
19 try:
20
21 self._observers.remove(observer)
22
23 except ValueError, e:
24
25 pass
26
27
28
29 def notify(self):
30
31 for observer in self._observers:
32
33 observer.update()
34
35
36
37 def listening(self):
38
39 pass
3.2.2 需要监听水龙头打开时间,所以继承实现了一个具体的监听器,OpenTimeListener,只需要实现listening函数即可。
1 class WaterTimeListener(Listener):
2
3 #使用观察者模式来实现对水龙头的监听
4
5 format = "llHHI"
6
7 size = struct.calcsize(format)
8
9 keyboardDevice = "/dev/input/event3"
10
11 fd = os.open(WaterDevice, os.O_RDWR)
12
13
14
15 def __init__(self, maxTime=300, leastUseTime=120, notifyInterval=60):
16
17 Listener.__init__(self)
18
19
20 self.maxTime = maxTime
21
22 self.lleastUseTime = leastUseTime
23
24 self.notifyInterval = notifyInterval
25
26 self.OpenTime = time.time()
27
28 self.Time = time.time() - notifyInterval
29
30 self.start_water_time = time.time()
31
32
33
34 def listening(self):
35
36 while True:
37
38 op = os.read(self.fd, self.size)
39
40 # timestamp, subsecond, type, code, value = struct.unpack(format, op)
41
42 now, _, _, _, _ = struct.unpack(self.format, op)
43
44
45 # 如果超过十分钟没有关,就开始通知
46
47 if now - self.lastNotifyTime >= self.notifyInterval:
48
49 self.notify()
50
51 self.lastNotifyTime = now
52
53 self.lastInputTime = now
3.2.3观察者抽象类Observer,这个其实只是个接口而已,不是一个类,因为update()虽然是共有的接口函数,但它的实体是因人而异的,没法在这个类里给出一个default的实现(对比一下Listener的notify方法就知道了)
1 class Observer:
2
3 def update(self):
4
5 raise NotImplementedError("Must subclass me")
3.2.4该项目,目前的发通知给使用者,只实现了一种方式:发送邮件给用户。
1 # 第三方 SMTP 服务
2
3 mail_host = "XXXX" # 设置服务器
4
5 mail_user = "XXXX" # 用户名
6
7 mail_pass = "XXXXX" # 口令
8
9
10 class Mail:
11 def __init__(self):
12
13 sender = 'XXXX'
14
15 receivers = ['XXXX'] # 接收邮件
16 message = MIMEText(body.encode('utf-8'), 'plain', 'utf-8')
17
18 message['From'] = Header("提醒", 'utf-8')
19
20 subject = '水龙头忘记关了吧!!!'
21
22 message['Subject'] = Header(subject, 'utf-8')
23
24 try:
25
26 smtpObj = smtplib.SMTP()
27
28 smtpObj.connect(mail_host, 25) # 25 为 SMTP 端口号
29
30 smtpObj.login(mail_user, mail_pass)
31
32 smtpObj.sendmail(sender, receivers, message.as_string())
33
34 print("邮件发送成功")
35
36 except smtplib.SMTPException:
37
38 print("Error: 无法发送邮件")
值得注意的是:一个监听器可以有多个观察者,而一般一个观察者只能有一个监听器。比如监听是否浪费水这个事件,可以有多种通知方式,比如响闹铃,发送通知到手机,记录使用情况到日志文件以备后续分析,等等。而一般一个观察者都是特化了的,不会用在多个监听器上面。
4 观察者模式给该项目带来了哪些好处
对于这个项目而言:观察者模式把监测事件和响应事件这两个职责给分离了,各司其职。监听器监测使用者有没有长时间打开水龙头,有的话,就通过接口告诉观察者,观察者再发通知给使用者,让使用者判断是否是忘记了关水。
即观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。观察者模式在观察目标和观察者之间建立一个抽象的耦合。且观察者模式符合“开闭原则”的要求。