Posts 设计模式-监听模式
Post
Cancel

设计模式-监听模式

概念解析

监听模式(观察者模式),核心是设计监听者和被观察者,当被观察者对象在状态或者内容数据发生变化时,能够通知所有的监听者对象(被动/主动)相应的变化,然后监听者对象能够做出相应的反应。其本身属于“一对多”的关系,也有很多名称:“发布/订阅模式”、“模型/视图模式”、“源/监听器模式”等。监听者模式的类图如下: img 上图中, Subject 为被观察者抽象类, Observer 为监听者抽象类, ConcreteObserver 为监听者具体实现, ConcreteSubject 为被观察者具体实现。其中被观察者的核心方法是:添加监听者、移除监听者、通知监听者的方法。而监听者至少存在的一个方法:更新方法,做出相应处理。 设计上的细节:

  • 设计过程要注意抽象出:谁是观察者和被观察者
  • 被观察者(Subject)在通知的时候,不需要指定监听者,监听者可以自主订阅

设计模板

监听模式的代码框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from abc import ABCMeta, abstractmethod
# 引入ABCMeta/abstractmethod 定义抽象类和抽象方法

class Observer(metaclass=ABCMeta):
    """观察者基类"""

    @abstractmethod
    def update(self, observable, object):
        pass


class Observable:
    """监听者基类"""

    def __init__(self):
        self.__observers = []

    def addObserver(self, observer):
        self.__observers.append(observer)

    def removeObserver(self, observer):
        self.__observer.remove(observer)

    def notifyObservers(self, object=None):
        for o in self.__observers:
            o.update(self, object)

实例分析

登陆异常的检测和提醒,实现的功能如下:当账号异常登陆的时候,能够以短信/邮件形式将登陆信息发送给对应的号码和邮箱。

代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import time
# 导入时间处理模块

class Account(Observable):
    """用户账户"""

    def __init__(self):
        super().__init__()
        self.__latestIp = {}
        self.__latestRegion = {}

    def login(self, name, ip, time):
        region = self.__getRegion(ip)
        if self.__isLongDistance(name, region):
            self.notifyObservers({"name": name, "ip": ip, "region": region, "time": time})
        self.__latestRegion[name] = region
        self.__latestIp[name] = ip

    def __getRegion(self, ip):
        # 由IP地址获取地区信息。这里只是模拟,真实项目中应该调用IP地址解析服务
        ipRegions = {
            "101.47.18.9": "浙江省杭州市",
            "67.218.147.69":"美国洛杉矶"
        }
        region = ipRegions.get(ip)
        return "" if region is None else region


    def __isLongDistance(self, name, region):
        # 计算本次登录与最近几次登录的地区差距。
        # 这里只是简单地用字符串匹配来模拟,真实的项目中应该调用地理信息相关的服务
        latestRegion = self.__latestRegion.get(name)
        return latestRegion is not None and latestRegion != region;


class SmsSender(Observer):
    """短信发送器"""

    def update(self, observable, object):
        print("[短信发送] " + object["name"] + "您好!检测到您的账户可能登录异常。最近一次登录信息:\n"
              + "登录地区:" + object["region"] + "  登录ip:" + object["ip"] + "  登录时间:"
              + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))


class MailSender(Observer):
    """邮件发送器"""

    def update(self, observable, object):
        print("[邮件发送] " + object["name"] + "您好!检测到您的账户可能登录异常。最近一次登录信息:\n"
              + "登录地区:" + object["region"] + "  登录ip:" + object["ip"] + "  登录时间:"
              + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))


def testLogin():
    accout = Account()
    accout.addObserver(SmsSender())
    accout.addObserver(MailSender())
    accout.login("Tony", "101.47.18.9", time.time())
    accout.login("Tony", "67.218.147.69", time.time())
This post is licensed under CC BY 4.0 by the author.

设计模式-状态模式

设计模式总结(Python)