【译文】抽象漏洞法则

翻译 :邹永胜

欢迎访问网易云社区,了解更多网易技术产品运营经验。  


互联网上有一个很关键的部分,你每天都依赖于它,它处于TCP协议中,它是互联网的基本组成部分之一。

TCP协议是可信赖的传输层协议。这就意味着你采用TCP去发送数据,不会存在数据混乱或丢失的情况。

我们日常中的很多事情都在使用TCP协议,例如浏览网页或者发送邮件。每一封邮件能够稳定的到达正是由于TCP的可靠性。尽管其中有许多垃圾邮件。

作为比较,另外一个不可靠的传输数据的协议称为 `IP`。没有人能够保证你发送的数据能够达到,即使到达了,数据也许已经混乱了。如果你采用IP协议去发送一批数据,如果只有一半数据到达了,千万别惊讶。即使是达到的数据中,也不能保证数据正确。

但是有个神奇现象:TCP是在IP协议之上的,TCP有义务采用一种不可信赖的方式去传输数据。

为了解释这个神奇的现象,在真实的世界中以道德的角度来思考这个问题:

想象我们要从百老汇运送一批演员到好莱坞,我们采用汽车运输的方式,横跨几个州。运输过程中,有些车辆坠毁了,可怜的演员就这样挂了。有些演员或醉酒、剪了头发、纹了鼻子等,形象变化了,以至于好莱坞不再接受他们演出。与此同时,由于每辆车的行驶路线不同,导致每个演员的到达时间不一样。现在假设有一种服务叫做好莱坞速递,它能快速安全有序的运送演员到好莱坞,每个演员的到达顺序和出发顺序完美地保持一致。神奇的部分在于好莱坞速运除了把演员塞进车里,然后横跨几个州运输外,没有其他办法运输。好莱坞速运会检验每个演员确保是否完好无损的到达,如果出现问题,就会请求总部,重新下发送一个与损坏的那个演员一模一样的演员过来。如果到达后演员顺序出错,速运公司会重新给他们排序。如果有不明飞行物撞击损坏了内华达州的道路,那么所有的运输车辆都会重新选择亚利桑那州的道路。当到达目的地后,速运公司不会告知加州的好莱坞导演们中途发生了什么,对于导演来讲,他的演员们仅仅是比之前慢一点而已,至于中午的UFO撞击事件他们压根不知道。

也就是说,TCP的神奇之处就是计算机科学家们喜欢称之为抽象的东西:掩盖复杂的流程达到简化的目的。事实证明,很多计算机编程都是由抽象构成的。什么是字符串库?这是一种使计算机能像操纵数字一样容易操作字符串的方法。什么是文件系统?这是一种假装硬盘驱动器不是一堆可以在特定位置存储二进制数据位的旋转磁盘,而是一个文件夹内文件夹的分层系统,其中包含单个文件,这些文件又由一个或多个字节串组成。

回到之前的TCP。早些时候,为了简单起见,我撒了个小谎,有些人现在耳中已经涌出了怒气,因为谎言使得你疯狂。之前我说TCP保证你的消息会到达。 实际上并非如此。 如果您的宠物蛇将连接计算机之间的网线咬断了,那么没有任何IP数据包可以通过,自然通过TCP方式传输的任何数据都到达不了,自然你的消息无法到达。 与此同时如果公司中的系统管理员惩罚你,将你的网络分配到一条过载的集线器上,只有部分IP数据包能够通过,尽管此时TCP可以工作,但是一切都会变得非常慢。

这就是我所称做的*抽象漏洞(leaky abstraction)*。TCP尝试在一条极不稳定的网络线路上提供稳定的服务,但是由于网路漏洞的限制,导致这种抽象并不能平稳的保护你的数据。这仅仅是我为了阐述抽象漏洞所阐述的一个例子:

> 某种意义上任何有意义的抽象都是由漏洞的
> - All non-trivial abstractions, to some degree, are leaky.

抽象失败。有时比较少,有时却很多。有存在漏洞的。有出错的。当你有抽象的时候,漏洞总会。下面是一些例子。

- 有时就像在大型二维数组上迭代,纯水平、垂直方向操作可能具有完全不同的性能,单纯的一个方向所产生的错误可能会大于操作另外一个方向。即使程序员假装他们拥有一个大的平面地址空间(实际上也是真是内存的一个抽象),当出现错误时,确定的物理空间提取数据比其他空间更耗时。
- SQL语言旨在抽象出查询数据库所需的过程性步骤,而只允许您定义所需的内容,并让数据库确定查询数据库的过程性步骤。但在某些情况下,某些SQL查询比其他等价查询查询慢数千倍。一个著名的例子是,即使结果集相同,如果指定“where a=b and b=c and a=c”,那么一些SQL服务器的速度要比只指定“where a=b and b=c”快得多。你不必关心程序,只需要规范。但是有时候抽象会泄露并导致糟糕的性能,你需要跳出查询计划分析器,研究它做错了什么,并找出如何使查询运行得更快。
- 即使像NFS和SMB这样的网络库允许您将远程机器上的文件“当作本地的”来对待,但有时连接变得非常慢或中断,文件停止工作就像本地一样,并且作为程序员,你必须编写代码来处理这个问题。“远程文件与本地文件相同”的抽象就泄露。这里是UNIX系统管理员的一个具体例子。如果您将用户的主目录放在NFS挂载的驱动器上(一个抽象),并且你的用户创建.forward文件以将其所有电子邮件转发到其他地方(另一个抽象),此时NFS服务器在新电子邮件到达时关闭,则不会转发消息,因为.forward文件找不到。抽象中的漏洞实际上引起了一些消息被抛弃。
- C++字符串类应该让你觉得字符串是一流的数据。他们试图抽象出字符串并让你其表现像整数一样简单。几乎所有C++字符串类都重载+运算符,因此可以编写S+“bar”来连接。但是你知道吗?不管他们怎么努力,在地球上没有C++字符串类,可以让你键入“foo”+“bar”,因为C++中的字符串文字总是char *,从来没有字符串。抽象出现了一个漏洞,语言不允许你插入。(有趣的是,随着时间的推移,C++演进的历史可以被描述为试图在字符串抽象中插入漏洞的历史。为什么他们不能只给语言添加一个本地字符串类)
- 下雨的时候你不能开得那么快,尽管你的车有挡风玻璃的雨刷、前灯、车顶和加热器,这些都是起到保护你的作用,让你不要担心下雨的事实,但是你仍旧得担心水上滑行。在英国,有时雨很大,你看不见前面很远的地方,所以在雨中需要放慢速度,因为天气永远不可能完全被抽象出来,因为抽象法则有漏洞。

抽象漏洞法则有问题的一个原因是,抽象并没有真正简化我们的生活。当我培训某人成为C++程序员时,如果我从来没有教过他们关于字符型指针和指针算法的话,而是能直接进入STL字符串,那就很好了。但是有一天他们会编写字符串拼接的时候,“foo”+“bar”,真正奇怪的事情就会发生,无论如何,我必须停下来教他们关于字符指针的所有知识。或者有一天,他们无法调用具有具有OUT LPTSTR参数的Windows API函数,直到他们了解了char*、指针、Unicode、wchar_t、TCHAR头文件。

在向某人教授COM编程时,如果我能教他们如何使用Visual Studio向导和所有代码生成特性就好了,但是如果出了什么问题,他们就不知道为什么以及怎么调试它、修复它。因此我要教他们所有关于IUnknown、CLSID、ProgIDS...,天哪,还有好多细节要讲!

在教授有关ASP.NET编程的知识时,如果我能教给他们,他们可以双击一些东西,然后在用户单击这些东西时编写在服务器上运行的代码,那就太好了。但是实际上,ASP.NET抽象了编写HTML代码以处理对超链接()的单击事件和处理对按钮的点击的具体细节。问题:ASP.NET设计者需要隐藏的细节是,无法从HTML的超链接提交表单。他们的做法是,通过编写几行JavaScript并将之附加到超链接来实现这一点。很明显,会有抽象的漏洞,如果最终端用户禁用了JavaScript,ASP.NET应用程序就不能正常工作,并且如果程序员不理解ASP.NET在这个行为背后抽象了什么,他们就不会知道什么地方出错了。

抽象漏洞法则意味着,每当有人提出一个全新的令人眼花缭乱的代码生成工具,它使我们工作变得高效,您就会听到很多人说“首先学习如何手动完成,然后使用自动化工具来节省时间。代码生成工具就是人们尝试去抽象一些工作,进行包装。这仅仅能够节省我们的工作时间,却不能节省我们的学习成本。

矛盾的是所有的这些都意味着,编程工具变得越来越高级,越来越抽象,但是这使得我们成为一个熟练的程序员变得更加困难。

在我第一次微软实习期间,我编写了在Macintosh上运行的字符串库。一个典型的任务:编写一个Strcat版本,执行结果会返回指向新字符串结尾的指针。几行C代码。这是正确的做法,这些是从K&R(一本关于C语言的很薄的书)中学习的。

今天,为了在CityDesk工作,我需要去了解Visual Basic、COM、ATL、C++、InnoSetup、Internet Explorer内部结构、正则表达式、DOM、HTML、CSS和XML。

十年前,我们可能已经想象到,新的编程范式将使编程变得更容易。事实上,这些年来我们创建的抽象确实使得我们处理软件开发中的复杂性变得简单,这些复杂性在10或15年前我们不必处理,比如GUI编程和网络编程。虽然这些很优秀的工具,像现代的面向对象的编程范式,让我们快速完成许多工作。但是突然有一天我们发现一个问题,是该种范式的抽象漏洞,修复它需要2周。并且当你需要雇佣一个程序员来使用VB编程时,很明显VB程序员是不够的,因为每次发现VB抽象漏洞时,他们都会完全陷入焦虑中。

抽象法则正在拖累我们。



本文章翻译自 Joel Spolsky 的 [The Law of Leaky Abstractions](https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/) 


免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击。 


相关文章:
【推荐】 最小化局部边际的合并聚类算法(上篇)
【推荐】 Spring Boot 学习系列(03)—jar or war,做出你的选择

你可能感兴趣的:(【译文】抽象漏洞法则)