Mediator 中介模式

设计模式 2020-05-19 4291 字 110 浏览 点赞

起步

中介模式属于行为型。中介类负责封装一组对象的交互,让类与类之间不再直接通信,而是通过中介类间接交流,从而降低程序的复杂度。

这种降低复杂度的方式多以:把一组对象之间的多对多关系,转化为一对多关系

需求

假设项目现下需要这样一种功能:磁盘里的配置信息与内存中的配置信息存在一种映射关系。要求,当更新内存中的配置信息时,磁盘中的配置文件同步更新;当修改磁盘里的配置文件时,内存中的配置信息同步更新。

先说明,为简化代码突出重点,下面将不会写监听磁盘变动的代码,而是用主动行为的 read 方法代替自动更新。

我会分为两个类,一个是对内存抽象的 Config,一个是对磁盘抽象的 Store:

class Config(object):
    """
        对应内存中的配置信息
    """
    ...

class Store(object):
    """
        对应磁盘中的配置信息
    """
    ...

完整代码如下:

class Config(object):
    """
        对应内存中的配置信息
    """
    def __init__(self, store: "Store"):
        self._data = {}
        
        self._store = store
        self._store.mount(self)

    def getv(self, key: str) -> Any:
        return self._data.get(key, None)

    def setv(self, key: str, value: Any):
        self._data[key] = value
        self._store.update_disk()  # 更新磁盘

class Store(object):
    """
        对应磁盘中的配置信息
    """
    def __init__(self,  cfg_path: str):
        self._cfg_path = cfg_path
        
        self._config = None
    
    def mount(self, config: Config):
        # 将 config 挂到 store 上
        self._config = config
        
    def update_disk(self):
        ... # 更新磁盘逻辑
        print("更新磁盘")

    def read(self):
        ... # 读取操作
        print("读取磁盘")
        data = {}
        self._config._data = data  # 更新内存

使用方式:

store = Store("xxx.json")
config = Config(store)
# 磁盘读取
store.read()
print("---- 分割 ----")
# 内存更新
config.setv("name", "zhong")

## 输出:
读取磁盘
更新内存
---- 分割 ----
更新磁盘

上述设计也许看不出多大问题来,这是因为在上述的假设场景里,并没有多对多的通信需求,而只是一对一(Config <-> Store),二者的调用关系还算明朗可辨。稍微麻烦一点的是,Config 实例中有 Store 的实例,而 Store 实例中绑定了 Config 实例,这是为了能够调用到彼此的方法。此外,作为共有变量 cfg_path(配置文件路径),放在 Config 中、Store 中都行。如果 cfg_path 在 Config 中,当 Store 需要时,必须 self._config._cfg_path 获取,反之则要 self._store._cfg_path。假设这样的共有变量很多,哪些要在 Config 中,哪些写在 Store 里都是比较麻烦的事儿。如果各自存放一些共有变量,不利于大脑记忆,需要用到什么变量时就要查看代码,确定其在哪个类中。

也许你还是看不出上述代码“不好”在哪里,那就有必要直接和中介模式的代码做个对比了。

中介模式

class Mediator(object):
    """ 中介 """
    def __init__(self, cfg):
        self._cfg = cfg
        
        self._config = None
        self._store = None
    
    def mount(self, obj, typ):
        if typ == "config":
            self._config = obj
        elif typ == "store":
            self._store = obj
        else:
            raise NotImplementedError
    
    def update_disk(self, data: dict):
        self._store.update_disk(data)  # 更新磁盘
    
    def update_memory(self, data: dict):
        self._config._data = data  # 更新内存

class Config(object):
    """
        对应内存中的配置信息
    """
    def __init__(self, mediator: Mediator):
        self._data = {}

        self._m = mediator
        self._m.mount(self, "config")
    
    def getv(self, key: str) -> Any:
        return self._data.get(key, None)

    def setv(self, key: str, value: Any):
        print("更新内存")
        self._data[key] = value
        self._m.update_disk(self._data)  

class Store(object):
    """
        对应磁盘中的配置信息
    """
    def __init__(self, mediator: Mediator):
        self._m = mediator
        self._m.mount(self, "store")
    
    def update_disk(self, data: dict):
        ... # 更新磁盘逻辑
        print("更新磁盘")

    def read(self):
        ... # 读取操作
        print("读取磁盘")
        data = {}
        self._m.update_memory(data)

使用方式:

mediator = Mediator("xxx.json")
config = Config(mediator)
store = Store(mediator)

store.read()
print("---- 分割 ----")
config.setv("port", 99)

## 输出:
读取磁盘
---- 分割 ----
更新内存
更新磁盘

在上述代码中,Mediator 类就是一个“中介”,由它直接与 Config、Store 对话,避免 Config、Store 之间过分耦合。从上述代码中也能看出来,Store 类更加清晰明了。并且公共变量 cfg 由中介持有,总之不管双方需要什么,都直接 call 中介类即可。

总结

中介模式的原理很简单,在代码中看不出多大效益来,但在现实世界里常常存在。

譬如在 qq 群里聊天,我们发送消息并非直接将信息发送到对方的手机上,而是先把信息发送到腾讯的服务器上,再由服务器把消息转发出去。这里的“服务器”就是一个中介,不但接管了用户之间的通信,还有可以添加敏感词屏蔽、历史记录等功能。再想想很早之前打个电话,先拨通接线员,然乎让接线员转到某个电话上去。接线员是中介。

中介模式使得两方的沟通更加简单灵活,但随着需要的功能越多,很可能造成中介类庞大复杂,这就尤其需要注意:引入中介是为了降低代码的复杂度。如果适得其反,不用也罢。

感谢



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论