# 软件应变——随需而变,适者生存
# 概念
抽象接口描述了一个类的最本质的行为特征,具体实现随时可能变化,
隐藏他们可以保证这种变动不会波及客户代码。
软件和硬件的区别,不仅是无形和有形之别,更是变化和固化之别。
所谓变化,值源代码随时可能因需而变。
一个软件的修改维护时间通常会大大超过编写时间,越复杂越成熟的程序越是如此。
# 编程的两个难点
其一是逻辑的复杂性
其二是需求的变化性
- 误区
许多程序员看重前置忽略后者,大部分时间花在需求解决方案上,而不是在选择解决方案上。
充满技巧的代码不仅难于理解易于出错,而且普适性低而受变化的冲击更大。
过度设计带来不必要的复杂和效率损失。
- 软件之软,体现在适应变化的能力
质量低下的软件并不是不能变化,而是在变化面前不能应付自如。
一般表现为僵硬、敏感而脆弱,一有风吹草动,往往东修西补,难免顾此失彼。
因此许多编程设计思想包括OOP都以提高应变能力为主题的,抽象和封装便是代表。
抽象一个对象模型是将一类对象最本质、最不易变化的部分提炼出来,
封装——更准确说是信息隐藏则是将非本质、容易变化的部分隐藏起来,从而将一个类划分为阴阳两面。
# 软件的变化大致分为两种
一种是内在需求而作的结构性变化,通常以改善软件质量为目的,
包括代码重构、性能调优(performance tuning),等等
一种是处于外在需求而作的功能性变化,通常以满足客户需求为目的。
理想的抽象与封装,应能完全避免第一类变化对客户代码的影响,
也能最大限度地降低第二类变化带来的副作用。
# 实现
桥梁模式(bridge pattern),可以将接口和实现完全分开。(P222)
在C++中常被称为Pimpl惯用法(Pointer to Implementation idiom),
也称为编译器防火墙惯用法、句柄类(handle classes)、不透明指针(opaque pointer)、柴郡猫(Cheshire Cat)等。
但严格来说,桥梁模式与Pimpl的侧重点不同,前者是将抽象从实现中解耦的一种设计模式,
后者主要是表现为一种减少编译依赖和编译时间的编程技巧。
# 原则
代码的改动量和改动范围当然越小越好,但更重要的是哪里该改动、哪里该不动。
- 开闭原则(open/closed principle,OCP)对扩展开放,对修改封闭。
软件应该在模块的基础上进行扩展,而不是修改。换言之,严格遵守开闭原则的软件,不应该改老代码,只能新增代码。
要实现这点,最关键还是抽象。抽象的模块因稳定而很少需要修改,因普适而便于扩展。
诚然百分之百不修改原有代码时过于理想化了,但越基础、越是核心的模块越应该遵守这个原则。
平时使用的类库、框架就是最好的例子。
如果类库提供的服务不能满足需求,客户一般通过接口(interface)、多态(polymorphism,[,pɒlɪ'mɔːfɪz(ə)m])、
继承(inheritance)、合成(composition)等技巧来扩展相关的类;
如果框架提供的服务不能满足要求,客户一般通过框架开放的各种可扩展的点来嵌入新模块。
无论哪种情况,需要修改的都是享受服务的客户代码,而不是提供服务的基础代码。
# 总结
软件要想有高度的可重用性,必须以高度的应变性为前提。
当然,这并不意味着类库和框架不能变,但这些变化是出于功能改善、性能改进等方面的需要,
不会因个别客户的具体需求而变,并且应以不破坏现有客户为原则的。