图 1 (根据 <<深入浅出设计模式>> 中文版 page 39 页图改)
问题的简单描述:
设计一个软件来显示气象站的数据.
系统分析:
1. 系统分析的目标是:将整个系统分解为若干个子系统,确保子系统间要松耦合,子系统内布局要紧凑。
分解方案:
1. 如上图所示, 整个系统分解为三个部分。气象站、weathe 对象、显示装置
2. 整个系统分解为二部分。气象站、显示装置(因为 weather 对象是不是一个实体, 在气象站和显示装置的交互中可以不考虑)
批判方案:
1. 用文字描述的方案总是会遗漏信息并给人误导。以上的两种方案都包括气象站、显示装置。看不出气象站只是对外表现,增加或移除感应装置会改变气象站的内部功能。
2. 以方案1 为例, 气象站和wether 对象,weather对象和显示装置间存在接口。在描述中看不出有接口的存在。
发散思维:
1. 假设需求上要求 weather 对象要定时更新
这种需求极容易误导人(找出这些极具有误导人的需求不是一件容易的事)
我第一次的设计是让一个函数定时地去获取相关信息并驱动显示装置更新。整个程序过于凸出“定时更新” 这一点,而且程序耦合严重。如果需求变化为 weather 对象要实时更新。整个程序就要重写了。
2. 假设需求上要求 weather 对象要实时更新
因为 weather 对象要实时更新, 所以采用观察者模式。程序松耦合,添加新的显示装置或感应装置都很容易。此时如果需求变化为 “定时更新”,可以很容易修改程序而不需要整个程序推倒重来。
Q1:需求上的“实时更新”和“定时更新”都只是一种具体的更新方式,为什么没有把它们当作个某个”未知的更新方式”来处理。如果只将它们考虑为“未知的更新方式”。以后如果需求有变化,那只是和“更新方式”有关的部分需要修改。
Q2:就目前的设计方案中,根本没有“更新方式”部分?
字面上是找不到任何 “更新方式”(因为实际上存在,这说明是方案做的不好。大部分的软件重构都有可能是前期的需求分析的不好,导致许多“xx部分”根本没有列上去,从而别人忽略)。“显示方式”本身就该是 “weather 对象”和 “显示设备”间的协议。
Q3: 做需求和设计软件,只考虑 实体的存在而忽略实体间的交互(只是用一根线表示这个 2 个模块有关系)。对我个人而言,以后要注意不要忽略”非实体的存在“
Q4:如果说已经绘出 UML 图, 为什么程序的设计方案还会如 Q1 和 Q2 中所讨论的那样,被需求上的某个特定描述所误导。难道 UML 图不能告诉 我们 各个模块的交互方式和关系。为什么不能根据 UML 图来设计软件,而把具体的需求当作是一些实际的策略。
UML 上怎么样的连线表示可以采用观察者,即某种模式的 UML 特征连线 (可以考虑用 软件来自动生成程序框架)
结构决定功能,高中化学知识!
发散思维2:
图 2 云图
sicp 的 Professor 讲述了某个关于数据封装云图的故事,然而我是听了就忘了。
Q1. 感应装置获取数据,数据经过 weather 对象和显示设备最终展示在用户目前。
假设某天更换了感应装置的类型,导致获取数据格式变化。然后每个部分都要修改。
为了避免这种情况。模块间要通过方法交互而不是直接获取数据(ood 沉思录中已讨论过 类的数据要私有)
Q2. 显示格式要变化了, 会不会也要修改呢。
图 3 从传感器到用户
Q3:系统分为多个多个模块,能不能在这些模块都加入透传中间层。
以后修改代码也就是修改中间层或添加中间层(估计在高性能应用场景下,估计是不可能的。但是有多少人的工作和高性能搭边呢)
观察者模式的关键字:
松耦合、事件驱动、观察者和订阅者
设计模式泛泛谈:
观察者模式和异步回调的关系。一个是模式,另外一个具体的设计方法
Python 中的观察者模式实现:
用 blinker 库