# 3.X 聊聊设计模式和程序设计 Author: ou_3840181b7c52ab34d2aa31f288fc7bc3 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); 但如果你很不幸,学习过一些设计模式,或者说有一些程序设计经验,你会开始考虑一下问题: 1. 我用什么语言实现?这个语言的代码文件组织有没有推荐的架构?(比如,第三方包引用了放在哪里?文件加密的 util 是放在内部还是封装成可供其他人引用的三方包?) 2. 选用什么样的依赖管理机制? 3. 编译最终产物的时候用什么工具,make 还是 maven 还是 gradle 还是 shell 还是 python?... 4. walk 能不能兼容不同平台的系统?在 win 下面能不能用? 5. 有没有什么设计模式可以用?(开始翻书) 6. 卧槽,用官方推荐的组织架构的话这个程序有点单薄啊,要不要加点功能上去 7. ... 要明白,拿着个锤子绝不能看什么都是钉子。 设计模式本身也是权衡的艺术,我们今天经常说的东西,比如 DDD,SOA,微服务,monorepo,也都是一种广义的设计模式。权衡什么呢?权衡的是:是要维持程序在发生功能变动时的可拓展性、降低程序维护复杂度,还是追求程序的快速实现。 拿上文的例子来说,如果你的所有程序都是在运行 Linux 上的,这个也只是给你自己快速检查文件完整性,之后也几乎不存在增加新的功能的可能,为什么不随便找个脚本语言快速十几行写完呢? ## 如何学习 个人认为,现在所谓的设计模式分两种,第一种是狭义的设计模式,就是各种设计模式的堆积。 此外,还有广义的设计模式,这块内容就很多了,但也是讨论如何降低代码复杂度的,包括: 1. 程序代码怎么组织(在开发 web 应用时这块经常有争议,可以参考 [https://cloud.tencent.com/developer/article/1837487](https://cloud.tencent.com/developer/article/1837487)) 2. 程序之间怎么组织(是放在一个编成个大的,还是微服务,还是用 FaaS 之类的变体) 3. 一些帮助减少程序复杂度的代码原则:[https://zhuanlan.zhihu.com/p/82324809](https://zhuanlan.zhihu.com/p/82324809) 这部分的学习不能操之过急。个人的建议是: 1. 学习一些设计模式,看完两次肯定没什么感觉,甚至会觉得看了个寂寞(可以先看看 Head first 设计模式) 2. 忘了他,去写你自己的代码,遇到复杂度诅咒了再考虑如何解决 3. 读他人的优秀代码,想一想这里他为什么要这么写,换成是你的话会怎么写,各自有什么利弊 4. 重复 1