写代码这件事,迈入第七个年头才有了一些心得(第二章 抽象)


写代码这件事,迈入第七个年头才有了一些心得(第二章 抽象)

一、抽象概念

眼见的自然形式会蒙蔽真实的本质,简化事物的形象,才能显露艺术的真实面目。

-- [荷兰]蒙德里安

抽象是一种思维,也是一种艺术,它是现实世界与机器语言之间的桥梁。

抽象就是在特定的环境,特定的视角,特定的维度,对事物进行分析、提炼、精简、归纳、推演、概括等行为,从具体到抽象,探索事物的规律和本质。

规避细节,提炼共性, 推演归集。下图是一幅展示牛的抽象图演进的示意图:

过往六七年的经历,也告诉了我一个道理,抽象思维是编程中最为重要的思维。

二、抽象要素

在软件领域,关于抽象,根据过往经验提炼了以下几个核心要素:

抽象之分层

分层架构的关键就在于层次的抽象,因此大型复杂业务系统,才能被软件所理解和解释。 这里的抽象分层,也有拆分,将整体分成局部之意。

  • 没有分层的软件是混乱的,是不灵活的,甚至生命周期也是短暂的。
  • 分层是降低软件的复杂度(也是降低耦合);同时分层也增强了软件的灵活度,也在变化时容易扩展。

软件应该被分层抽象。简单举几个例子:

  • 网络七层协议, 复杂的网络传输通信被拆分成多层
  • 软件应用中的三层架构模型。软件系统都是被分层实现的。

三层架构,每一层职责清晰,每一层都具备可扩展,如数据层,就可选择不同的存储介质。 而分层的关键在于抽象,做好边界划分,明确层级之间的职责,分而治之。

软件也应该被抽象成多层,抽象成多个模块组成,而不是又大又全的一个独立个体。

抽象的粒度

在谈粒度之前,先举两个例子:

例子一: 在计算机的世界,数据被抽象成 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产品业务属性更强,平台会弱化
立场、理念、用户群体等不同,抽象是不一样的,实现就差别更大了,比如各种权限

必须要明确和告诫自己,抽象有可能是错误的,是有陷阱的

  1. 抽象不应该放之四海而皆准,需要有细节,不要过度抽象; 也不要不抽象。有合理的粒度
  2. 抽象有主观色彩,有上下文,有角度;有维度;偏听则暗,兼听则明
  3. 抽象不是真理,甚至是错误的。

抽象既简单又不简单。那么如何进行抽象呢

三、正确抽象

抽象是站在更高地方看问题,它需要全面、清晰、正确

简单总结两个点:

  • 方法论和经验
  • 训练和演进

部分经验

    1. 自顶向下,自下而上。就像金字塔一样,一层层的
    1. 通用往下沉,个性往上浮,细节留扩展
    1. 下层不依赖上层,根据不依赖具体,不依赖细节
    1. 系统建模:从整体到局部; 业务建模:从局部到整体
    1. 由粗到细,由大到小,保持一定的秩序
    1. 更多的场景输入,用例驱动
    1. 多参与一些业务、数据建模,积累经验
    1. 学习,总结,实践
  • .......

警惕:所有别人的经验都是别人的!!!

一个故事: 毕业的第二年,针对一个业务做 ER 图设计,错误的分了一个对对多的场景,经导师指导修正了;同时也给我留下了深刻印象。抽象可能是错误的;也可能需要修正。感谢那些给我指导的大佬们......

代码应用

如何将抽象应用到编码中,那些经典的范式,早已被提炼总结出来了,如下所示:

就像 SpringBoot 中的所有扩展点,皆是面向接口编程,而变得异常灵活。如果你一直写重复的代码,不知道程序如何扩展,可能还没有学会抽象。

✒️下篇内容,会考虑抽象在代码中的实践应用。

四、最后总结

本文主要从抽象的概念,抽象的要素,再到如何抽象编写。其实抽象本身也很抽象。

  • 分层、粒度、角度、边界是关键要素。
  • 警惕抽象陷阱
  • 抽象是一个演进的过程
  • 抽象是一种思维
  • ......

工作进入第七年,才慢慢明白一个道理,编程拼的不是技巧,而是思想,而抽象思想是其中之一

推荐阅读

📚最后关于抽象能够联想的两本书籍。

书籍 描述
《金字塔原理》 讲解总分结构,结构化思维,金字塔结构等
《代码大全》 如何写好代码,非常实用

写代码这件事,迈入第七个年头才有了一些心得(第一章 关注点分离)

相关推荐
Moonbit6 分钟前
用MoonBit开发一个C编译器
后端·编程语言·编译器
Reboot1 小时前
达梦数据库GROUP BY报错解决方法
后端
稻草人22221 小时前
java Excel 导出 ,如何实现八倍效率优化,以及代码分层,方法封装
后端·架构
渣哥1 小时前
原来 Java 里线程安全集合有这么多种
java
间彧1 小时前
Spring Boot集成Spring Security完整指南
java
掘金者阿豪1 小时前
打通KingbaseES与MyBatis:一篇详尽的Java数据持久化实践指南
前端·后端
间彧1 小时前
Spring Secutiy基本原理及工作流程
java
对象存储与RustFS2 小时前
Spring Boot集成RustFS十大常见坑点及解决方案|踩坑实录
后端
RoyLin2 小时前
TypeScript设计模式:原型模式
前端·后端·node.js
菜鸟谢2 小时前
Manjaro Tab 无自动补全
后端