写代码这件事,迈入第七个年头才有了一些心得(第二章 抽象)
一、抽象概念
眼见的自然形式会蒙蔽真实的本质,简化事物的形象,才能显露艺术的真实面目。
-- [荷兰]蒙德里安
抽象是一种思维,也是一种艺术,它是现实世界与机器语言之间的桥梁。
抽象就是在特定的环境,特定的视角,特定的维度,对事物进行分析、提炼、精简、归纳、推演、概括等行为,从具体到抽象,探索事物的规律和本质。
规避细节,提炼共性, 推演归集。下图是一幅展示牛的抽象图演进的示意图:

过往六七年的经历,也告诉了我一个道理,抽象思维是编程中最为重要的思维。
二、抽象要素
在软件领域,关于抽象,根据过往经验提炼了以下几个核心要素:

抽象之分层
分层架构的关键就在于层次的抽象,因此大型复杂业务系统,才能被软件所理解和解释。 这里的抽象分层,也有拆分,将整体分成局部之意。
- 没有分层的软件是混乱的,是不灵活的,甚至生命周期也是短暂的。
- 分层是降低软件的复杂度(也是降低耦合);同时分层也增强了软件的灵活度,也在变化时容易扩展。
软件应该被分层抽象。简单举几个例子:
- 网络七层协议, 复杂的网络传输通信被拆分成多层

- 软件应用中的三层架构模型。软件系统都是被分层实现的。
三层架构,每一层职责清晰,每一层都具备可扩展,如数据层,就可选择不同的存储介质。 而分层的关键在于抽象,做好边界划分,明确层级之间的职责,分而治之。
软件也应该被抽象成多层,抽象成多个模块组成,而不是又大又全的一个独立个体。
抽象的粒度
在谈粒度之前,先举两个例子:
例子一: 在计算机的世界,数据被抽象成 0 和 1 ; 0 和 1 高度抽象,对于没有计算机背景的人来讲,用 0 和 1 来表达数据,因为信息细节少,所以理解晦涩。 虽然 0 和 1 被抽象得如此简单; 但要形成一个可传输的数据包,却需要经过复杂的协议组装。
发送报文 | TCP报文 |
---|---|
![]() |
![]() |
例子二,在计算机中要理解乘法的实现,首先需要先学习源码、补码、反码;再到定点数的表示;再到定点数的加法减法、再到乘法,这样才能从计算机的角度理解1 X 1 = 1
。
通过这两个例子,告诉我们一个道理,所有的抽象是有粒度的(层次): 抽象的粒度越高(层次),泛化能力越强,但丢失的细节也越多。就像 0 和 1 一样, 要表达任何一个符号都需要复杂的组装。
简而言之,抽象是有粒度(层次),需要有权衡判断。
- 过度抽象,丢失细节,信息少
- 不抽象,累赘,成本高,复用性差,信息多

"万物皆对象", 就像在 Java 中,可以使用 Object 表示所有的类; 但是要访问对象内部的信息,却需要强制转换。但如果毫无抽象,代码会呈现爆炸式增长,最后臃肿累赘,无法维护。
放之四海而皆准的抽象是简单的,信息量是很少的; 抽象就应该允许细节,有特定属性特征;同时给他们留有扩展。
抽象的极致等于没有抽象!!! 就像下图一样,从五边形,到消失的点,随着抽象的高度化,信息越来越少,最后消失。

可以将抽象的粒度理解为抽象的层次、抽象的程度;另外抽象的层次越高,也越好理解,抽象的层次越低则越难理解,举例说一个经验:
- 在阅读学习一个新框架、新仓库代码时。首先应该从层级较高入手,即先了解设计,因为这一层抽象层次高,容易理解,信息也精炼;其次再查看流程;最后再看代码细节。不要一上来就一头扎进代码细节里面。这样会理解变得换乱,增加学习成本,甚至可能把自己绕晕,困于其中。

开发中需先有设计,再有编码,如果一上来就编码,容易造成混乱,千万不可本末倒置!!!
抽象的角度
横看成岭侧成峰,远近高低各不同。
抽象是按照不同维度和视角进行总结归纳的。视角不同,维度不同,抽象出来的概念就是有区别的。
还记得小学课本《画杨桃》杨桃被画成五角星那篇文章,它也告诉了我另外一个道理,抽象是有角度和立场的。
不妨再尝试说一下下面这些物体的相同点和不同点:

- 都是家具
- 都是没有生命
- 形状不同
- 面向不同
- .....
不同的人会说出不一样的观点。抽象,是有维度、角度、立场的。
一个在互联网行业荒诞又无处不在的故事......
背景:客户想知道本月产品销售额,如果能用图表查看就更好了。
产品思维 | 研发平台思维 |
---|---|
定制开发 1 周上线,让报表看起来美观一些,注重用户体验。 | 做一个报表引擎平台,这样再来新需求,简单配置一下就能复用 |
总有那么一部分人,总想着什么都往平台、往引擎做。 又常常搞个半吊子;最后用户不买单,因为客只想要一个指标数据,而不是平台。而这群人却还妄图忽悠客户购买他们的平台呢......
回归正题: 抽象有很强的主观性,有视角、维度和立场,因此它有局限性,上下文。因为这些因素的存在,抽象可能是错的。
抽象的边界
软件需要边界,抽象也需要有边界。
- 边界不清楚,容易内部混乱; 边界不清楚,容易职责模糊。
- 边界常常是容易引起变化的部分,是造成软件雪崩的罪魁祸首。
DDD 和 三层架构混合
一些插曲故事:应用程序开始用DDD,部分同学对模型抽象不够清晰,边界模糊,分层最后混乱; 最后 DDD 又回到了三层架构。模块之间十分混乱,新手同学一脸懵逼,竟然不知道代码写在何处,最后只能还笑着说:能跑的代码就是好代码😂😂😂😂😂😂

边界不清晰,会导致混乱。
经过上面几个点的分析,总结提炼了一个点:抽象是有陷阱的。
抽象的陷阱
那些细节变化点需要特别注意,因为往往是导致我们系统需要重构的点。甚至推翻重来。
错误的立场,它可能让你推倒重来,付出惨痛教训。
一个真实的案例:也是一个惨痛的案例。前期想做 saas 产品,后期又说做成平台。最后无疾而终,白白辛苦 6 个月......
saas产品 & 平台一些区别 |
---|
设计建模上,抽象的层次不一样,平台抽象层次更高 |
业务属性上:saas产品业务属性更强,平台会弱化 |
立场、理念、用户群体等不同,抽象是不一样的,实现就差别更大了,比如各种权限 |
必须要明确和告诫自己,抽象有可能是错误的,是有陷阱的
- 抽象不应该放之四海而皆准,需要有细节,不要过度抽象; 也不要不抽象。有合理的粒度
- 抽象有主观色彩,有上下文,有角度;有维度;偏听则暗,兼听则明
- 抽象不是真理,甚至是错误的。
抽象既简单又不简单。那么如何进行抽象呢
三、正确抽象
抽象是站在更高地方看问题,它需要全面、清晰、正确
简单总结两个点:
- 方法论和经验
- 训练和演进
部分经验
-
- 自顶向下,自下而上。就像金字塔一样,一层层的
-
- 通用往下沉,个性往上浮,细节留扩展
-
- 下层不依赖上层,根据不依赖具体,不依赖细节
-
- 系统建模:从整体到局部; 业务建模:从局部到整体
-
- 由粗到细,由大到小,保持一定的秩序
-
- 更多的场景输入,用例驱动
-
- 多参与一些业务、数据建模,积累经验
-
- 学习,总结,实践
- .......
警惕:所有别人的经验都是别人的!!!
一个故事: 毕业的第二年,针对一个业务做 ER 图设计,错误的分了一个对对多的场景,经导师指导修正了;同时也给我留下了深刻印象。抽象可能是错误的;也可能需要修正。感谢那些给我指导的大佬们......
代码应用
如何将抽象应用到编码中,那些经典的范式,早已被提炼总结出来了,如下所示:

就像 SpringBoot 中的所有扩展点,皆是面向接口编程,而变得异常灵活。如果你一直写重复的代码,不知道程序如何扩展,可能还没有学会抽象。
✒️下篇内容,会考虑抽象在代码中的实践应用。
四、最后总结
本文主要从抽象的概念,抽象的要素,再到如何抽象编写。其实抽象本身也很抽象。
- 分层、粒度、角度、边界是关键要素。
- 警惕抽象陷阱
- 抽象是一个演进的过程
- 抽象是一种思维
- ......
工作进入第七年,才慢慢明白一个道理,编程拼的不是技巧,而是思想,而抽象思想是其中之一。
推荐阅读
📚最后关于抽象能够联想的两本书籍。
书籍 | 描述 |
---|---|
《金字塔原理》 ![]() |
讲解总分结构,结构化思维,金字塔结构等 |
《代码大全》![]() |
如何写好代码,非常实用 |