《软件设计的哲学》阅读摘要之设计原则

server/2024/12/26 0:06:17/

在这里插入图片描述

软件设计的哲学》(A Philosophy of Software Design)是一本在软件架构与设计领域颇具影响力的书籍,作者 John Ousterhout 在书中分享了诸多深刻且实用的软件设计理念。书中列举的这些设计原则,汇聚了作者丰富的实战经验与深邃的思考,给软件开发者们提供了高屋建瓴的指导,助力大家在项目中打造出更具可维护性、扩展性的软件系统。
本书名称叫做“哲学”,其实更像是一本指导书,里面的具体原则比经常听到的“强内聚,弱耦合,单一原则“等等,更加具有实践指导意义。这里列举一些重要的原则要点16条:

序号原则名称
1复杂性是渐进的,需关注细节(Complexity is incremental; you have to sweat the small stuff)
2工作代码并非足够(Working codes isn’t enough)
3持续进行小的投入以改进系统设计(Make continual small investments to improve system design)
4模块应具有深度(Modules should be deep)
5设计接口应使常见用法尽可能简单(Interfaces should be designed to make the most common usage as simple as possible)
6模块拥有简单接口比简单实现更重要(It’s more important for a module to have a simple interface than a simple implementation)
7通用目的模块应更具深度(General-purpose modules are deeper)
8分离通用目的和特定目的的代码(Separate general-purpose and special-purpose code)
9不同层次应具有不同的抽象(Different layers should be different abstractions)
10将复杂性向下拉(Pull complexity downward)
11定义错误使其不存在(Define errors out of existence)
12设计两次(Design it twice)
13注释应描述代码中不明显的内容(Comments should describe things that are not obvious from the code)
14软件应设计为易于阅读,而非易于编写(Software should be designed for ease of reading, not ease of writing)
15软件开发的增量应是抽象,而非功能(The increments of software development should be abstraction, not features)
16区分重要事项与不重要事项,并着重关注重要的部分(Separate what matters from what doesn’t matter and emphasize the things that matter.)

1. 复杂性是逐步累积的;你必须关注细节 (Complexity is incremental; you have to sweat the small stuff)

  • 含义与理解:软件系统的复杂性并非一蹴而就,而是像滚雪球一般,从项目伊始的细微之处慢慢积攒起来。起初看似微不足道的小决策、小代码片段,随着系统不断拓展与迭代,可能引发一系列连锁反应,使整体复杂度飙升。忽略细节,就如同埋下一颗颗定时炸弹,随时可能引爆大规模的技术难题。
  • 示例:开发一款餐饮外卖 APP,最开始在设计订单备注功能时,如果没仔细考虑字符长度限制、特殊字符过滤这些小细节,短期内或许不影响使用。但当业务量增大,遇到一些恶意输入或者超长备注需求时,就可能导致数据库存储出错、订单处理流程紊乱,后续修复涉及多个模块联动,复杂性大幅提升。

2. 能运行的代码还远远不够 (Working codes isn’t enough)

  • 含义与理解:仅仅实现代码的基本运行功能,只是迈出软件开发的第一步。软件并非一次性产品,后续的升级、维护以及功能拓展需求源源不断。若初期只为“跑起来”而仓促编码,忽略架构的合理性、代码的可扩展性与可维护性,后续项目生命周期里,每一次优化与新增功能都将举步维艰。
  • 示例:一个简单的个人博客网站,早期用最简易的代码拼凑实现了文章发布和浏览。但随着博主想要添加社交分享按钮、评论区实时互动、多设备适配等功能时,由于一开始没有采用模块化架构、分层设计,代码搅成一团乱麻,新增功能的难度呈指数级增长,甚至可能要推翻重来。

3. 持续投入小成本来改进系统设计 (Make continual small investments to improve system design)

  • 含义与理解:系统设计并非一劳永逸,而是需要在开发全程,不间断地投入少量精力与资源去优化。这些微小投入看似不起眼,却能在长期积累下,让系统的架构愈发稳固、高效,从容应对业务的动态变化,避免后期因架构老化而付出高昂的重构代价。
  • 示例:以一款日程管理 APP 为例,每次小版本迭代时,开发团队不只是忙着堆砌新功能,还会抽出部分时间重构数据库查询语句,优化界面渲染逻辑。虽然每次改进的效果不那么显著,但随着时间推移,当用户量持续攀升,APP 在处理海量日程数据时依然流畅,新功能也能毫无阻碍地整合进去。

4. 模块应当有深度 (Modules should be deep)

  • 含义与理解:模块的深度意味着它不应仅仅提供表面、单薄的功能,而是要深入挖掘业务需求,将相关的复杂功能聚合、强化,形成一套完整且强大的能力体系。有深度的模块更具内聚性,对外输出稳定、高效的服务,能显著提升整个系统的处理能力与灵活性。
  • 示例:在视频编辑软件里,特效添加模块不是简单罗列几种预设特效,而是深挖视频处理算法,融合色彩校正、光影变幻、动态跟踪等技术。无论是制作炫酷的短视频,还是专业影视剪辑,它都能深度满足创作者对画面特效的精细调控需求。

5. 接口的设计应使最常见的用法尽可能简单 (Interfaces should be designed to make the most common usage as simple as possible)

  • 含义与理解:接口作为模块与外界交互的桥梁,其设计优劣直接影响系统集成的难易程度。聚焦于最普遍的使用场景,把接口设计得简洁易懂,能极大降低其他模块与之对接的成本与门槛,提升开发效率,促进整个系统各部分的协同工作。
  • 示例:对于地图导航 SDK,众多开发者最常使用的功能就是定位与路径规划。优秀的 SDK 接口设计,只需开发者传入起点、终点位置信息,用极少的代码就能获取精准路线,屏蔽掉后台复杂的地图数据解析、算法优化过程,让接入过程轻松流畅。

6. 对于一个模块而言,拥有简单的接口比拥有简单的实现更重要 (It’s more important for a module to have a simple interface than a simple implementation)

  • 含义与理解:模块内部实现或许涉及复杂高深的算法与逻辑,但对外部使用者而言,他们无需关心这些内部细节。提供简单、清晰的接口,能够隐藏内部复杂性,让其他模块轻松调用,实现系统的低耦合,便于不同团队并行开发,保障整体架构的灵活性与扩展性。
  • 示例:在人工智能图像识别模块中,内部可能运用了深度卷积神经网络,训练过程繁杂且算力消耗巨大。但对外暴露的接口,仅需接收图片路径,就能直接返回识别结果,把复杂的运算封装起来,其他开发者不用钻研图像识别底层技术,就能快速集成该功能。

7. 通用模块更具深度 (General-purpose modules are deeper)

  • 含义与理解:通用模块旨在服务多种不同的业务场景,需要应对各式各样的需求变化,因此必须深挖功能、容纳丰富逻辑,构建起强大的通用能力。通用性越强,模块内部整合的知识、技术就越密集,深度也就自然显现出来。
  • 示例:在各类软件开发项目里,日志记录模块属于通用模块。它不仅要记录基本的时间、事件信息,还要支持不同级别日志分类、多线程安全写入、远程日志同步等复杂功能,以适配从桌面小程序到大型分布式系统的全方位需求。

8. 区分通用代码与专用代码 (Separate general-purpose and special-purpose code)

  • 含义与理解:通用代码具备复用潜力,能在多个不同场景、模块中发光发热;专用代码则服务于特定业务需求,针对性强。明确划分二者,能让代码库条理清晰,便于维护与管理,一方面提高通用代码的复用率,另一方面精准优化专用代码,互不干扰。
  • 示例:电商平台的商品展示页面,有通用的图片加载、布局渲染代码,这些可以抽离成通用模块,应用到其他商品分类甚至不同电商项目。而针对限时折扣商品特有的倒计时、闪烁特效代码,属于专用代码,单独封装,方便后续修改折扣逻辑,不影响通用展示模块。

9. 不同的层级应有不同的抽象 (Different layers should be different abstractions)

  • 含义与理解:软件架构分层构建,各层级肩负不同使命,对应不同的抽象层次。底层靠近硬件或基础资源,抽象程度低,处理具体事务;高层则聚焦业务逻辑,抽象程度高,从宏观视角统筹调度。合理分层与抽象,让系统层次分明,易于理解、开发与维护。
  • 示例:在企业级 ERP 系统里,底层数据库访问层直接操作数据表,执行增删改查,极为具象;中间业务逻辑层将底层操作抽象成订单处理、库存管理等业务单元;最上层的用户界面层,从用户视角抽象出便捷的操作流程与可视化界面,各层级各司其职。

10. 降低复杂性 (Pull complexity downward)

  • 含义与理解:随着软件系统的成长,复杂性悄然攀升,而开发者要主动出击,运用合理的架构调整、代码重构等手段,把复杂的逻辑梳理清晰,隐藏不必要的细节,下沉复杂实现,让核心业务流程简洁明了,提升系统的稳健性与可读性。
  • 示例:一款在线文档编辑工具,初始版本为兼容多种文档格式,代码充斥着大量格式转换的复杂判断与嵌套逻辑。后期重构时,把格式转换部分封装成独立底层模块,上层编辑操作只调用简洁接口,简化了编辑流程,降低了整体复杂性。

11. 将错误消灭在定义阶段 (Define errors out of existence)

  • 含义与理解:在软件开发前期,无论是需求分析、架构设计,还是接口定义环节,都要严谨地梳理规则、限定边界,提前预料可能出现的错误场景,并制定防范措施。从源头上把控,远比在测试、上线后再来处理错误成本更低、效果更好。在实现过程中,也要注意,一旦出现异常,就地解决,尽量减少向外或者向上抛出,减少异常处理的链条,减少复杂性。
  • 示例:开发在线考试系统,在需求定义时就明确规定答题时间格式、答案提交规则,代码编写伊始便严格校验输入合法性。如此一来,考试过程中因格式错误、非法提交引发的系统故障就能被扼杀在萌芽状态。

12. 进行二次设计 (Design it twice)

  • 含义与理解:软件首次设计受限于时间、认知等因素,很难尽善尽美。在获取一定的用户反馈、积累实际运行数据后,进行二次设计,基于真实场景重新审视架构、优化流程,能让软件更贴合市场需求,弥补前期不足,延长软件生命周期。
  • 示例:出行打车 APP 初次上线时,为抢时间匆忙确定了派单算法与司机乘客匹配机制。运营一段时间,收集大量数据与用户投诉后,二次设计优化算法,综合考虑距离、路况、司机服务评价等因素,大幅提升用户打车体验。

13. 注释应当描述那些从代码中看不出来的内容 (Comments should describe things that are not obvious from the code)

  • 含义与理解:代码虽然是程序员之间沟通的主要方式,但有些关键决策背景、设计意图仅凭代码难以呈现。注释就起到补充说明的作用,为后续维护者、协作者点明代码背后隐藏的业务考量、技术权衡,避免他人花费大量时间去揣测代码意图。
  • 示例:一段加密算法代码里,选用了一种非标准加密方式。代码里只能看到算法实现流程,而注释详细说明是因为项目特定的安全合规要求、数据传输环境限制,才采用该小众算法,让接手者瞬间明晰缘由。

14. 软件设计应着眼于便于阅读,而非便于编写 (Software should be designed for ease of reading, not ease of writing)

  • 含义与理解:软件开发是团队协作活动,一份代码在项目存续期会历经多人之手。易于阅读的代码,遵循规范命名、清晰分层、简洁逻辑,能让新成员迅速融入,理解业务流程,降低沟通成本。过于追求编写时的个人便利,会牺牲代码可读性,给后续维护带来巨大困扰。
  • 示例:大型开源项目代码库,变量命名全部采用语义化词汇,函数按功能模块清晰分组,代码注释详尽。即使新手加入团队,顺着代码结构与注释,也能较快搞清楚核心逻辑,投入开发工作。

15. 软件开发的增量应该是抽象,而非功能特性 (The increments of software development should be abstraction, not features)

  • 含义与理解:单纯不断堆砌新功能,会让软件陷入无序扩张,臃肿不堪。以抽象作为增量,意味着从现有功能中提炼通用模式、架构模式,用更高级的抽象框架去整合功能,提升软件的柔韧性与扩展性,从容应对未来变化。
  • 示例:一款音乐播放 APP,初期陆续上线本地播放、在线播放、歌单创建等功能。后续开发若以抽象为导向,提炼出媒体资源管理抽象层,就能轻松兼容新的音频格式、流媒体协议,而不是孤立地添加一个个播放功能。

16. 区分重要事项与不重要事项,并着重关注重要的部分 (Separate what matters from what doesn’t matter and emphasize the things that matter.)

  • 含义与理解:软件项目资源有限,无论是开发时间、人力,还是服务器算力。精准甄别核心业务流程、关键用户需求,把资源集中投入,保障关键部分稳定、高效运行,适当弱化次要环节,才能在资源约束下实现最优产出。
  • 示例:电商大促期间,电商平台最关键的是保障订单提交、支付成功、库存扣减这些核心链路顺畅无阻,页面上一些非关键的广告位更新、个性化推荐微调等非核心事务可以暂缓,集中火力服务好抢购用户。

http://www.ppmy.cn/server/153173.html

相关文章

从零开始使用MaxKB打造本地大语言模型智能问答系统与远程交互

文章目录 前言1. 下载运行Ollama2. 安装大语言模型3. 安装Cpolar工具4. 配置公网地址5. 固定公网地址6. MaxKB 添加Olama7.创建问答应用 前言 目前大语言模型(LLM)已经成为了人工智能领域的一颗璀璨明星,从自然语言处理到智能问答系统&#…

VS2022 中的 /MT /MTd /MD /MDd 选项

我们有时编译时,需要配置这个 运行库,指定C/C++运行时库的链接方式。 如下图 那么这些选项的含义是什么? /MT:静态链接多线程库 /MT选项代表“Multi-threaded Static”,即多线程静态库。选择此选项时,编译器会从运行时库中选择多线程静态连接库来解释程序中的代码,…

Docker 安装 禅道-21.2版本-外部数据库模式

Docker 安装系列 1、拉取最新版本(zentao 21.2) [rootTseng ~]# docker pull hub.zentao.net/app/zentao Using default tag: latest latest: Pulling from app/zentao 55ab1b300d4b: Pull complete 6b5749e5ef1d: Pull complete bdccb03403c1: Pul…

微信小程序中momentjs无法切换中文问题处理

微信小程序中momentj.s无法切换中文问题处理. 表现为 使用 locale(“zh-cn”)无效。 处理方法 # 1、先删除 miniprogram_npm\moment\index.js # 2、将 node_modules\moment\min\moment-with-locales.min.js 复制到 miniprogram_npm\moment下 并重命名为index.js # 3、修改mi…

CTFHub disable_functions通关

LD_PRELOAD 来到首页发现有一句话直接就可以用蚁剑连接 根目录里有/flag但是不能看;命令也被ban了就需要绕过了 绕过工具在插件市场就可以下载 如果进不去的话 项目地址: #本地仓库;插件存放 antSword\antData\plugins 绕过选择 上传后我们点进去可以看到多了一个绕过的文件;…

CVE-2015-7611

适用于Apache James Server 2.3.2 默认安装 账户密码为root root 靶场 solidstate 也适用,但是不能直接获得shell Apache James :Apache James 简称 James, 是 Java Apache Mail Enterprise Server的缩写。James 是100%基于Java的电子邮件服务器。它…

50.pow(x, n) python

pow 题目题目描述示例 1:示例 2:示例 3:提示: 题解解题思路python代码解释提交结果 题目 题目描述 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即, x n x^n xn )。 示例 1&…

麒麟系统修改配置镜像源地址并安装openGL

1.编辑文件/etc/apt/sources.list 进入目录 cd /etc/apt/ 编辑文件(需要root权限) sudo vi sources.list 将镜像地址改为你指定的镜像地址 #deb http://archive.kylinos.cn/kylin/KYLIN-ALL 10.1 main restricted universe mul tiverse #deb http:…