3.X 聊聊设计模式和程序设计
Author: yyl Last revised 2022/08/07
前言
在开始讲内容之前,我觉得我得首先聊一聊到底为什么我们需要这些东西。为什么我们会需要设计模式呢?
这个问题并不可笑,也不幼稚,事实上,很多东西红极一时,最终却被历史证明是不被需要的。类似的事情太多了,可以是一种理念,比如日心说,可以是一种产品,比如软驱、RAMBUS 内存,等等。历史已经丢弃了太多不被需要的东西了。
学习本身是有成本的事情,如果在开始之前能够理解为什么要开始就更好,尽管很多时候我们做不到这件事,但也不能忘了这件事。
上个世纪 80~90 年代的时候,人们试图通过 “设计模式” 这一工具,抽象总结所有的软件设计方法,构建一个无敌的知识库。最终使得软件工程就像堆积木一样简单,避免软件随时间变得杂乱的熵增问题,但毫无疑问这是失败的,你永远无法抽象总结所有东西,设计模式的巅峰,可能是在 JDK 之中。在阅读 Java 自身提供的诸多 API 时,你会发现其中蕴藏了大量的设计模式,而且是赤裸裸地把使用了的设计模式写在了类名中,如 “XXXListener”,很少有另一个语言也是这样的。
设计模式并没有完全成功,但也没有完全失败。他现在以一种 “术语” 的形式存在,帮助人们理解常见的编程范式。—— 这是什么意思呢?举个例子,如果你尝试和同事说明你的一个设计时,你说,它首先有两个类,一个类提供了一个通用的接口,可以被另一个类注册,另一个类则负责把所有的动作通知给已经注册的类。这种说法显然非常繁琐,对方也未必能理解你的意思。但如果你说,这里使用了监听者模式,如果你的同事碰巧学习过一些设计模式,他就能立即理解你的意思。即使他没有学习过相关知识,只要简单百度一下也能理解个大概。这是当今设计模式的一个重要作用。
另一个重要作用则是它的本源了,用于总结抽象程序设计的范式。但并不是说要你写的所有代码能用设计模式的就都用设计模式,这更多是提供给你一种选项,让你能够充分权衡。关于这部分,下文还会有说明。
目前的设计模式,主要集中在如何避免程序复杂度上。
什么是程序复杂度呢?就是说你今天写了个软件,明天给他加功能的时候发现这个程序不怎么可维护,于是直接在之前的逻辑上加了个 if,你的一个 if 通常不会有什么大问题,程序复杂性问题是日积月累的。随着大家都在不同的地方写自己的 if,整个程序最终会变得不可维护。可能一个微小的改动会导致很多地方的 if 出现不符合预期的判断,可能终有一天有一个需求无法再用 if 来实现,等等。
为了解决这个问题,设计模式使用的方法是原则,通过制定一系列的原则,并确保大家都遵守,来避免代码腐烂。
拿个锤子,见什么都是钉子
设计模式,或者说广义的程序设计架构的初学者,很多都会想去设计一个 “完美的架构”。
当就像计算机程序设计本身是关于权衡的艺术一样,学习程序架构也不能看见什么都上架构。下面举例子具体说明。
你要设计一个小程序给自己用,这个程序的作用是定时读取一边本地硬盘上的全部文件,通过 sha256 算法生成每个文件的文件摘要,比对文件是否遭到篡改,如果遭到了篡改就报个警。
如果你没有学习过这些乱七八糟的设计模式,我想你大概会这么做:
main():
walk("/");
walk(String path):
For file : os.listfile(path):
// 检查文件摘要
For dir : os.listdir(path):
walk(dir);
但如果你很不幸,学习过一些设计模式,或者说有一些程序设计经验,你会开始考虑一下问题:
- 我用什么语言实现?这个语言的代码文件组织有没有推荐的架构?(比如,第三方包引用了放在哪里?文件加密的 util 是放在内部还是封装成可供其他人引用的三方包?)
- 选用什么样的依赖管理机制?
- 编译最终产物的时候用什么工具,make 还是 maven 还是 gradle 还是 shell 还是 python?...
- walk 能不能兼容不同平台的系统?在 win 下面能不能用?
- 有没有什么设计模式可以用?(开始翻书)
- 卧槽,用官方推荐的组织架构的话这个程序有点单薄啊,要不要加点功能上去
- ...
要明白,拿着个锤子绝不能看什么都是钉子。
设计模式本身也是权衡的艺术,我们今天经常说的东西,比如 DDD,SOA,微服务,monorepo,也都是一种广义的设计模式。权衡什么呢?权衡的是:是要维持程序在发生功能变动时的可拓展性、降低程序维护复杂度,还是追求程序的快速实现。
拿上文的例子来说,如果你的所有程序都是在运行 Linux 上的,这个也只是给你自己快速检查文件完整性,之后也几乎不存在增加新的功能的可能,为什么不随便找个脚本语言快速十几行写完呢?
如何学习
个人认为,现在所谓的设计模式分两种,第一种是狭义的设计模式,就是各种设计模式的堆积。
此外,还有广义的设计模式,这块内容就很多了,但也是讨论如何降低代码复杂度的,包括:
- 程序代码怎么组织(在开发 web 应用时这块经常有争议,可以参考 浅析整洁架构之道 (二) 初步了解 The Clean Architecture)
- 程序之间怎么组织(是放在一个编成个大的,还是微服务,还是用 FaaS 之类的变体)
- 一些帮助减少程序复杂度的代码原则:设计模式之 SOLID 原则
这部分的学习不能操之过急。个人的建议是:
- 学习一些设计模式,看完两次肯定没什么感觉,甚至会觉得看了个寂寞(可以先看看 Head first 设计模式)
- 忘了他,去写你自己的代码,遇到复杂度诅咒了再考虑如何解决
- 读他人的优秀代码,想一想这里他为什么要这么写,换成是你的话会怎么写,各自有什么利弊
- 重复 1