把 java-design-patterns.com 的「编程原则」页整理成一份中文速览:按简洁、解耦、内聚、扩展、契约、质量、定律七组归类,每条给出核心、为什么与怎么做,并保留原文出处便于回溯。它们不是教条,而是判断设计取舍时的常用透镜。
本文整理自 java-design-patterns.com/zh/principles/(其材料源自 webpro/programming-principles)。原页按字母顺序平铺了 26 条原则,本文按主题重新归为七组,便于成体系地阅读与检索。
| 组 | 主题 | 条数 | 包含 |
|---|---|---|---|
| 一 | 简洁与直接 | 3 | KISS · YAGNI · 做最简单可行之事 |
| 二 | 分离与解耦 | 6 | 关注点分离 · 最小化耦合 · 得墨忒耳 · 组合优于继承 · 正交性 · 隐藏实现细节 |
| 三 | 内聚与职责 | 4 | 最大化内聚 · 单一职责 · 柯里定律 · 接口隔离 |
| 四 | 扩展与替换 | 3 | 开闭原则 · 里氏替换 · 封装变化 |
| 五 | 契约与协作 | 3 | 鲁棒性原则 · 控制反转 · 命令查询分离 |
| 六 | 质量与维护 | 4 | 避免过早优化 · DRY · 为维护者编码 · 童子军规则 |
| 七 | 工程定律 | 3 | 墨菲定律 · 布鲁克斯法则 · 林纳斯定律 |
用最少的机制表达最直接的意图——复杂度是成本,不是能力证明。
大多数系统,在保持简单而非刻意复杂时,运行得最好。
在确有必要之前,不要实现任何功能。
只做真正解决问题所需要的最简方案——来自极限编程(XP)的传统准则。
本组是原页的重心(6 条)。核心只有一个:让「不该相互影响的东西」彼此无关,使变更的影响面可控。
把程序拆成各司其职的部分,每一部分只处理一个关注点——例如「业务逻辑」与「用户界面」是两个关注点:改界面不应要求改业务逻辑,反之亦然。
Dijkstra(1974)称之为「有效组织思维的唯一切实手段」。原文:Separation of Concerns
耦合是模块之间相互依赖的程度——代码单元 A 变更后,代码单元 B「坏掉」的概率高低。耦合越低越好。
「别和陌生人说话。」一个对象只应与直接协作方交互,避免 a.b.c.d 式的链式调用。
优先用组合(持有并委托)表达复用,而非继承。
概念上不相关的事物,在系统中也不应彼此相关。
模块通过提供接口来隐藏信息(实现细节),不泄露任何不必要的信息。
与「解耦」相对的另一半:让「本就该在一起的东西」牢牢聚在一处,并让它只有一个变更理由。
模块的内聚,是其各项职责构成一个有意义的整体的程度。内聚越高越好。
一个类永远不应有超过一个变更理由。
把「胖接口」拆成多个更小、更贴近具体客户端的接口。接口应更依赖「调用它的代码」,而非「实现它的代码」。
如何在不改坏既有代码的前提下延展系统:对扩展开放、让子类型可替换、把变化点封装起来。
软件实体(如类)应当对扩展开放、对修改关闭——无需改动源码,即可修改其行为。
程序中的对象,应能被其子类型的实例替换,而不改变该程序的正确性。
好的设计能识别出最可能变化的热点,并把它们封装在 API 之后;当预期的变化发生时,修改被限制在局部。
模块之间如何「打交道」:接收输入与发送输出的宽严尺度、控制权归属、以及方法与副作用的界限。
对自己所做的要保守,对他人所给的要宽容。
又称「好莱坞原则」——别找我们,我们会找你。定制代码从通用框架那里获取控制流;可复用代码与问题特定代码即使协同工作,也各自独立开发。
每个方法要么是执行动作的命令,要么是返回数据的查询,二者不可兼得——提问不应改变答案。
代码会被读的次数远多于被写的次数——本组四条都指向同一个现实:维护才是主战场。
在知道瓶颈之前就优化,是浪费甚至有害的。高德纳:约 97% 的情况下,「过早优化是万恶之源」。
系统中每一处知识,都应有唯一、无歧义、权威的表达。
if 语句、数学公式、元数据等,只放一处。维护是任何项目中最昂贵的阶段——所以代码要为后来接手的人而写。
「离开营地时,让它比你来时更干净。」——每次改动,都应让代码比被发现时更干净。
三条跨越具体技术、描述「人与项目」客观规律的法则。原页把它们列在原则页中,本质上是不可违背的经验规律。
凡可能出错的,终将出错。
原文:Murphy's Law
向一个已经延期的软件项目增加人手,只会让它更晚完成。
原文:Brooks's Law
足够多的眼睛,就能让所有缺陷都变得浅显。
原文:Linus's Law
26 条一句话速览,按分组排列。
| 编号 | 中文名 | 英文名 | 一句话 |
|---|---|---|---|
| P01 | 保持简单 | KISS | 系统在保持简单时运行得最好。 |
| P02 | 你不会需要它 | YAGNI | 必要之前不要实现。 |
| P03 | 做最简单可行之事 | Do The Simplest Thing | 只做真正解决问题的最简方案。 |
| P04 | 关注点分离 | Separation of Concerns | 每个部分只处理一个关注点。 |
| P05 | 最小化耦合 | Minimise Coupling | 模块间相互依赖越低越好。 |
| P06 | 得墨忒耳定律 | Law of Demeter | 别和陌生人说话,避免链式调用。 |
| P07 | 组合优于继承 | Composition Over Inheritance | 用组合复用,少用继承。 |
| P08 | 正交性 | Orthogonality | 不相关的事物在系统中也不应相关。 |
| P09 | 隐藏实现细节 | Hide Implementation Details | 只暴露接口,不泄露实现。 |
| P10 | 最大化内聚 | Maximise Cohesion | 模块职责应构成有意义的整体。 |
| P11 | 单一职责原则 | SRP | 一个类只有一个变更理由。 |
| P12 | 柯里定律 | Curly's Law | 每段代码只做一件事。 |
| P13 | 接口隔离原则 | ISP | 拆胖接口,不强迫依赖无用方法。 |
| P14 | 开闭原则 | OCP | 对扩展开放,对修改关闭。 |
| P15 | 里氏替换原则 | LSP | 子类型可替换基类型而不破坏正确性。 |
| P16 | 封装变化 | Encapsulate What Changes | 把易变点封装在 API 之后。 |
| P17 | 鲁棒性原则 | Postel's Law | 发送保守,接收宽容。 |
| P18 | 控制反转 | Inversion of Control | 别找我们,我们会找你。 |
| P19 | 命令查询分离 | CQS | 方法非命令即查询,不可兼得。 |
| P20 | 避免过早优化 | Avoid Premature Optimization | 先跑通,profiling 后再优化。 |
| P21 | 不要重复自己 | DRY | 每处知识只有一个权威表达。 |
| P22 | 为维护者编码 | Code For The Maintainer | 维护是最贵阶段,为接手者写。 |
| P23 | 童子军规则 | Boy-Scout Rule | 每次提交让代码更干净一点。 |
| P24 | 墨菲定律 | Murphy's Law | 凡可能出错的终将出错。 |
| P25 | 布鲁克斯法则 | Brooks's Law | 给延期项目加人只会更晚。 |
| P26 | 林纳斯定律 | Linus's Law | 足够多的眼睛,缺陷皆浅显。 |