起步
中介模式属于行为型。中介类负责封装一组对象的交互,让类与类之间不再直接通信,而是通过中介类间接交流,从而降低程序的复杂度。
这种降低复杂度的方式多以:把一组对象之间的多对多关系,转化为一对多关系。
需求
假设项目现下需要这样一种功能:磁盘里的配置信息与内存中的配置信息存在一种映射关系。要求,当更新内存中的配置信息时,磁盘中的配置文件同步更新;当修改磁盘里的配置文件时,内存中的配置信息同步更新。
先说明,为简化代码突出重点,下面将不会写监听磁盘变动的代码,而是用主动行为的 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 群里聊天,我们发送消息并非直接将信息发送到对方的手机上,而是先把信息发送到腾讯的服务器上,再由服务器把消息转发出去。这里的“服务器”就是一个中介,不但接管了用户之间的通信,还有可以添加敏感词屏蔽、历史记录等功能。再想想很早之前打个电话,先拨通接线员,然乎让接线员转到某个电话上去。接线员是中介。
中介模式使得两方的沟通更加简单灵活,但随着需要的功能越多,很可能造成中介类庞大复杂,这就尤其需要注意:引入中介是为了降低代码的复杂度。如果适得其反,不用也罢。
感谢
- 参考 极客时间王争老师的《设计模式之美:中介模式》
- 参考 Mediator pattern
还不快抢沙发