任何使你的系统变得不易被理解、不易被修改的事物。
表现在我们的场景里,比如:
一些可能的解决方法
战术性编程 vs 战略性编程
战术性编程
优点: 以最快的速度完成可工作的功能
缺点: 以长期的视角来看, 容易积累技术债,增加复杂度
战略性编程
优点: 不仅仅可以工作, 还要易于修改、拓展
缺点: 每个功能需要时间去设计、去思考
书中给的答案是: 10% ~ 20%
一个理想的世界里,每一个模块应该完全独立于其他模块,工程师们应该可以修改、运行它们而不用知道别的模块的存在。
呵呵,理想是丰满的、现实是骨感。大家醒醒。
我们的模块往往必须知道某些信息,才能被运作,而这些东西最后都成为了依赖。
接口用于表达一个模块是什么,能做什么事,而不应该描述它如何做这些事。(同时,作为使用者,我们也不应该关注它们)。
在很大程度上,接口的概念可以帮助我们去屏蔽不相关性。
实现则恰恰相反,由一系列的代码组成,而最后这些代码相互运作,可以保证完成 interface 向用户 (工程师) 做出的承诺。
这些代码可能是异构的、可能是晦涩。但对于实现而言,最重要的是保证行为的一致。
一个好的设计里,接口应该要比它的实现要简单的多。
抽象是一个被 剥离了不重要 的内容的实体的概览
而往往,大部分的设计中,抽象这个概念本身都以 interface 的形式被表现。
一个抽象的好坏,取决于你对一个模块究竟可以正确的 剥离出多少 不重要的 内容
比如对于 Nodejs 而言,异步就是一种抽象,它抽象了 kernel 的真实的 IO 行为, 并且屏蔽了它的性质,开发者不需要知道它的真实平台是什么。 windows / unix ? overlapped io / select etc。这些东西都是不重要的。
但相应的,将接口暴露成异步风格是重要的。它被用于表达: 什么时候,某件事真的被做完了。这些信息对于开发者而言则是必须的。
一个好的模块:
Deep module Example:
Unix basic system call for I/O
(5个调用用了一辈子)
Garbage Collection
(直接消灭了所有的内存管理相关的复杂度)
Shallow module Example:
private void addNullValueForAttribute(String attr) {
data.push(attr, null)
}
(函数并没有提供任何抽象,名字更长了,但从目的来看不如直接操作数据来的直接)