2019年/11月/01日

首页回退

抽象必然泄露

这是一个定律,好遗憾我知道得太晚了,早点明白这个定律,早点能够醒悟工作中的那么多烦心事,下面这个列表曾经让我无比困惑:

比如这个SQL怎么这么慢?
怎么ES的数据平衡代价这么高?
怎么隔离级别有这么多套路?
怎么Hibernate如此的复杂?你知道OSIV吗?
实现一个Redis锁为什么如此之难?
为什么Seam死了?
Java的泛型的坑你了解多少?
构造一个可靠的线程池怎么也很难?
你能优雅的用SQL查询ES,Solr,Hadoop?
你想通过一个代码生成器来解决复杂业务问题?
你会写Js吗?Function.constructor.constructor.constructor == Function,这个你能理解吗?
NIO就一定比BIO高级?
不引入一个Node?你还能愉快的写前端?
一个logger.debug("")可以把系统弄挂掉
为什么大量的开发不知道equlas和hashcode的关系?
@Transactional标在方法上的巨大恐怖性
啥叫伪共享?
你看到的真的是事实吗?

这个列表还有很长,等我退休了把他全部打印出来回忆,哈哈.

如果你还没觉得抽象泄露有多可怕,看看CPU的bug:链接

你被浮点折磨过吗?

Double.MAX_VALUE == Double.MAX_VALUE + 100 //true
0.99999999f==1f //true
0.9f==1f //false

我一直讨厌SpringBoot和Mybatis,也是因为受到这个定律的影响,因为抽象必然泄露。 Effective Java整本书,都是在警告我们,需要不做什么,类似这样的书还有《Java安全编码标准》,这些书背后都是抽象泄露定律作怪。

怎么办呢?我们只能寻找简洁透明的东西,因为越简洁泄露可能才会越少,要不做什么, 因为不做什么比做什么更有学问
透明要理解为足够的透明和足够的不透明,因为当足够的不透明之后就等于透明了,比如CPU

这个定律的提出者是:Joel Spolsky, 出自于他的书:Joel on Software,中文《软件随想录》 以下是原文的摘取,发表于2002年,要二十年了。


TCP协议的神奇之处,计算机科学家们通常会将其称作为“抽象”:将复杂的问题用简单的方式表现出来。事实上,很多计算机编程工作都是在进行抽象。字符串库做了什么? 它能让我们觉得计算机可以像处理数字那样处理文字。 文件系统是什么?它让硬盘不再是一组高速旋转的磁性盘块,而是一个有着目录层级结构、能够按字节存储字符信息的设备。

我们继续说TCP。刚才我打了一个比方,有些人可能觉得那很疯狂。但是,当我说TCP协议可以保证消息一定能够到达,事实上并非如此。如果你的宠物蛇把网线给咬坏了,那即便是TCP协议也无法传输数据; 如果你和网络管理员闹了矛盾,他将你的网口接到了一台负载很高的交换机上,那即便你的数据包可以传输,速度也会奇慢无比。

这就是我所说的“抽象泄漏”。TCP协议试图提供一个完整的抽象,将底层不可靠的数据传输包装起来,但是,底层的传输有时也会发生问题,即便是TCP协议也无法解决,这时你会发现,它也不是万能的。 TCP协议就是“抽象泄漏定律”的示例之一,其实,几乎所有的抽象都是泄漏的。这种泄漏有时很小,有时会很严重。下面再举一些例子:

抽象泄漏引发的麻烦之一是,它并没有完全简化我们的工作。当我指导别人学习C++时,我当然希望可以跳过char *和指针运算,直接讲解STL字符串类库的使用。 但是,当某一天他写出了 “foo” + “bar” 这样的代码,并询问我为什么编译错误时,我还是需要告诉它char *的存在。或者说,当他需要调用一个Windows API, 需要指定OUT LPTSTR参数,这时他就必须学习char *、指针、Unicode、wchar_t、TCHAR头文件等一系列知识,这些都是抽象泄漏。

在指导COM编程时,我希望可以直接让大家如何使用Visual Studio的代码生成向导。但将来如果出现问题,学员面对这些生成的代码会不知所从,这时还是要回过头来学习IUnknown、CLSID、ProgIDS等等。天呐!

在指导ASP.NET编程时,我希望可以直接告诉大家双击页面上的控件,在弹出的代码框中输入点击响应事件。的确,ASP.NET将处理点击的HTML代码抽象掉了,但问题在于, ASP.NET的设计者需要动用JS来模拟表单的提交,因为HTML中的标签是没有这一功能的。这样一来,如果终端用户将JS禁止了,这个程序将无法运行。初学者会不知所措,直至他了解ASP.NET的运作方式, 了解它究竟将什么样的工作封装起来了,才能进一步排查。

由于抽象定律的存在,每当有人说自己发现了一款新的代码生成工具,能够大大提高我们的编程效率时,你会听很多人说“先学习手工编写,再去用工具生成”。代码生成工具是一种抽象, 同样也会泄漏,唯一的解决方法是学习它的实现原理,即它抽象了什么。所以说抽象只是用于提高我们的工作效率的,而不会节省我们的学习时间。

这就形成了一个悖论:当我们拥有越来越高级的开发工具,越来越好的“抽象”,要成为一个高水平的程序员反而越来越困难了。

我在微软实习的第一年,是为Macintosh编写字符串处理类库。很普通的一个任务:编写 strcat 函数,返回一个指针,指向新字符串的尾部。几行C语言代码就能实现了,这些都是从K&R这本C语言编程书上学习到的。

如今,我在CityDesk供职,需要使用Visual Basic、COM、ATL、C++、InnoSetup、Internet Explorer原理、正则表达式、DOM、HTML、CSS、XML等等,这些相对于古老的K&R来说都是非常高级的工具, 但是我仍然需要用到K&R的相关知识,否则会困难重重。

十年前,我们会想象未来能够出现各种新式的编程范型,简化我们的工作。

的确,这些年我们创造的各类抽象使得开发复杂的大型软件变得比十五年前要简单得多,就像GUI和网络编程。现代的面向对象编程语言让我们的工作变得高效快速。
但突然有一天,这种抽象泄漏出一个问题,解决它需要耗费两星期。
如果你需要招录一个VB程序员,那不是一个好主意,因为当他碰到VB语言泄漏的问题时,他会变得寸步难行。
抽象泄漏定律正在阻碍我们前进。