Skip to content

Software Design Fundamentals

什么是软件设计(Software Design)?为什么软件设计很重要?软件设计的原则是什么?软件设计的模式有哪些?这些都是软件设计的基础问题,这里及后续章节将逐一解答这些问题。

软件设计的重要性

代码质量有很多评价指标,比如可读性、可测试性、可维护性、可扩展性、可重用性、可扩展性等等。对于一个项目来说,至关重要的是软件的整体结构(structure)。好的结构最终会影响到上述的各种质量指标。软件设计的目标就是为了构建一个好的结构,从而提高软件的质量。好的软件设计能够容忍某些糟糕的实现决定,反之则不行,差的软件设计不能靠堆砌新的语言特性来弥补。

变化是软件的常态,是所有问题的根源。软件设计的目标就是为了更好地应对变化。

大部分的开发者应该遇到过这样的情况:当需要修改一个很小的功能或者修复一个 bug 时,却需要修改很多文件,涉及整个系统的多个模块。这就是依赖(dependency)问题。不管项目大小,依赖都是最核心的问题。因此,软件设计是一门管理组件之间依赖关系的艺术,目的是最小化人为的、技术性的依赖,并引入必要的抽象与折中。软件设计,或者说软件工程(engineering),更像是一门艺术,或者说是一种工艺(craft),与科学相结合。有的时候,软件架构(software architecture)这个术语,有时表达的也是类似的意思,可以互换,它们的目的是相同的。也有的人将架构和设计区分开来,认为架构是更高层次的设计。

我们可以将软件开发分成两个大层,第一层是设计层(design level),包含架构和设计,第二层是代码层(code level)。最底层是实现细节(implementation details),这一层有一些常见的实现模式(implementation patterns),比如 RAII、Pimpl、SFINAE(Substitution Failure Is Not An Error)等等,这些模式是 C++ 特有的,其他语言可能没有或者有不同的实现方式。再往上是软件设计,这一层包含一些设计模式(design patterns),比如装饰者、策略等等,这些就是语言无关的了。再往上是软件架构,如前所述,这里的共识不多,这里通常涉及关键决策。这一层包含一些架构模式(architectural patterns),比如微服务、客户端-服务器等。两者的共同点都是解决整体结构和相互依赖的问题。架构是更高层次的设计,解决的是模块、组件级别的,而设计则是更细粒度的,解决的是类、函数级别的。

惯用法(idiom)的出现使得实现和设计两者的界限更加模糊一些。惯用法表示某种模式(pattern),在某种语言中被广泛使用,比如前面提到的 RAII 就是 C++ 中的一种惯用法,还有 copy-and-swap 也是 C++ 中的一种惯用法。大部分的 C++ 惯用法都是实现层次的,设计层次的惯用法相对较少一些,比如 NVI(Non-Virtual Interface)和 Pimpl 就是一种设计层次的惯用法,分别脱胎于设计模式中的模板方法(Template Method)和桥接模式(Bridge Pattern)。