HackerTube English?
软件边界与变化 →

切开代码,就切开了依赖吗?

从一条要发邮件的规则开始。先把判断和执行分开,再检查分层、对象和抽象:改动一条业务规则时,哪些地方还是必须一起改?

本文的问题 · 9
  1. 把可以独立的职责真正拆开
  2. 把副作用集中起来,让其余行为更容易推理
  3. 不必更换语言,也能隔离副作用
  4. 值的语义,如何落实为模块边界?
  5. 形式分层不足以消除语义纠缠
  6. 小组件与值边界都在减少彼此耦合
  7. 提防错误抽象,也要让必要抽象足够深
  8. 都在写类,却对面向对象给出不同建议
  9. 从识别结构纠缠,退回到审视构思

把可以独立的职责真正拆开

Simple Made Easy 建议辨认原本可独立却被纠缠的部分;Boundaries 将计算核心与命令式外壳分开,是可以用来检验这一原则的具体架构例子。

对照原文与时间戳 · 2
start seeing interconnections between things that could be independent
"Simple Made Easy" - Rich Hickey (2011) · ▶ 59:28–59:32
an imperative shell that surrounds the functional core.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 14:48–14:54

这是跨资源分析,不是讲者宣称的继承关系;拆层的价值取决于真实依赖,而不是增加模块数量。

把副作用集中起来,让其余行为更容易推理

Overwatch 的 ECS 讲解指出主要行为副作用集中在一个调用侧;Bernhardt 则把决策留给函数式核心,并由命令式外壳处理数据库和邮件。合读可看到两种架构都通过明确副作用边界,降低理解和测试时需要追踪的交互范围。

对照原文与时间戳 · 2
all major behavioral side effects are localized to that call side
Overwatch Gameplay Architecture and Netcode · ▶ 18:20–18:26
an imperative shell that surrounds the functional core.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 14:48–14:54

ECS 的系统调用组织与函数式核心/命令式外壳不是同一种模式;不能据此认为所有副作用都应放进单一函数或单一文件。

不必更换语言,也能隔离副作用

函数式是否主流的演讲把语言流行与无突变风格区分开;Boundaries 在 Ruby 中用值驱动的核心处理决策、命令外壳承接 I/O,提供无需先改语言的具体做法。

对照原文与时间戳 · 2
no language features are required to program this style
Why Isn't Functional Programming the Norm? – Richard Feldman · ▶ 36:07–36:18
the expired users class makes all the decisions and the sweeper class has all the dependencies.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 15:28–15:38

Ruby 不会自动禁止突变;该设计靠边界纪律,且外壳仍保留必要副作用。

值的语义,如何落实为模块边界?

Hickey 强调不可变和语义透明;Bernhardt 展示以值作为交互边界,减少同步方法调用及测试替身的耦合。前者解释值的性质,后者给出组织代码的具体策略。

对照原文与时间戳 · 2
values are immutable right and values are semantically transparent
The Value of Values with Rich Hickey · ▶ 10:43–10:54
we now have values as boundaries.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 10:17–10:24

值并不自动保证整个系统纯函数化;I/O 仍留在外壳。两位讲者没有在这些片段中直接互相回应。

形式分层不足以消除语义纠缠

Simple Made Easy警告分开类与层次并不自动带来简单;Maybe Not用schema示例指出把数据形状和调用场景的可选性绑定会造成复用困难,修正需要分离这两个语义维度。

对照原文与时间戳 · 2
it's a mistake to put optionality in aggregate definitions
Maybe Not - Rich Hickey · ▶ 30:36–31:48
don't associate simplicity with partitioning and stratification, right?
"Simple Made Easy" - Rich Hickey (2011) · ▶ 34:50–35:20

不主张任何模块拆分都无效;Maybe Not讨论特定信息约束建模。

小组件与值边界都在减少彼此耦合

Metz 建议把代码拆成更小的东西;Bernhardt 说明以值作为对象边界,可以替代组件之间的同步方法调用。合读可把“缩小组件规模”和“选择低耦合交互方式”看作两个不同但互补的边界设计手段。

对照原文与时间戳 · 2
make smaller things
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 0:54–0:58
we now have values as boundaries.
Ruby Conf 12 - Boundaries by Gary Bernhardt · ▶ 10:17–10:24

小类或小方法不自动形成良好边界;值边界的好处取决于交互是否适合通过数据传递表达。

提防错误抽象,也要让必要抽象足够深

Metz 提醒保留重复有时比维护错误抽象便宜;Ousterhout 则强调深模块以简单接口隐藏大量能力。把两者并读可帮助判断抽象质量:抽象不应只是消除重复,而应真正提供稳定、足够有价值的能力边界。

对照原文与时间戳 · 2
It is far cheaper to keep duplication than it is to have to mess with the wrong abstraction.
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 14:52–14:59
very simple interface with a very large amount of functionality underneath it
A Philosophy of Software Design | John Ousterhout | Talks at Google · ▶ 14:24–14:29

两位讲者没有互相回应。Metz 反对的是错误抽象,并未主张避免一切抽象;Ousterhout 也没有说每段重复代码都应被抽成模块。

都在写类,却对面向对象给出不同建议

Metz 建议把类、方法和相互依赖变小;另一位讲者主张转向过程式风格,却明确说问题并不在类本身。两个片段帮助区分语言构造与组织代码的方式。

对照原文与时间戳 · 2
make smaller things
RailsConf 2014 - All the Little Things by Sandi Metz · ▶ 0:54–0:58
the problem is really not classes per se
Object-Oriented Programming is Bad · ▶ 1:37–1:42

不能从标题推断两人对每种类设计都直接对立;这些片段不构成相互回应。

从识别结构纠缠,退回到审视构思

Simple Made Easy 让我们检查概念之间不必要的关联;Hammock 则提醒许多严重问题源于错误构思。两者把关注点从更快地写出代码移向先理解问题与结构。

对照原文与时间戳 · 2
start seeing interconnections between things that could be independent
"Simple Made Easy" - Rich Hickey (2011) · ▶ 59:28–59:32
most of the big problems we have with software are problems of misconception
Hammock Driven Development - Rich Hickey · ▶ 4:27–4:36

两段分别谈系统结构与问题求解,不可把所有构思错误归因为耦合,也不是效率提升的实证证明。

读完再想一步

画出一次规则变更会波及的地方,再看文件边界是否与它重合。