我眼中的匈牙利命名法
上个月Linus通过Linux内核邮件列表一怒为注释,要求内核开发者“get rid of thebrain-damaged stupid networking comment syntax style”。估计Linus早就对网络协议栈代码里面不伦不类的注释风格颇有微词。当有人存在即合理式的要求沿用这种注释风格时,他终于爆发了,火力全开的喷了一顿。最后号召大家看到类似风格怪异的注释,顺手改之。终于找到提交内核代码的捷径!窃喜窃喜:)
窃喜之余,想到匈牙利命名法又是何等的相似。要求弃用匈牙利命名法的固网新规范已推出许久,但匈牙利命名法依然大行其道。新规范遇冷的一个原因正是已有代码大多都使用这种命名法,于是就像协议栈的情况一样,开发者自然而然的沿用匈牙利命名法。如果使用新的命名方式,势必会出现两种命名法共存,这该如何是好?
在寻找破冰的方法之前,我们先拨开历史的迷雾,看看匈牙利命名法到底是何方神圣。
匈牙利命名法(下文简称为HN,即HungarianNotation)的雏形始于无类型语言BCPL,后经匈牙利裔美国人Charles Simony引入到开发Office的微软应用软件部,因其单词排列顺序类似古怪的匈牙利姓名而得名,后来也称作应用型匈牙利命名法(Apps Hungarian,下文简称为AHN)。而后AHN被曲解引入到系统软件部,形成了系统型匈牙利命名法(SystemsHungarian,下文简称为SHN)。再随着Windows的方兴未艾,四处开花。以及各种开发文档的推波助澜,特别是广为流传的《Windows 程序设计》第一版大量使用匈牙利命名法,SHN逐渐成为Windows开发的事实标准。但是随之而来的也有反对的声浪,并在.net第一次发布时达到了高潮。微软在.net框架的《通用命名约定》中明确说明不建议使用匈牙利命名法(原文为DO NOTuseHungarian notation)。而后《Windows 程序设计》第六版也已经不怎么使用匈牙利命名法了。
为什么SHN会这么不受待见呢?我们先看看SHN是怎样一种命名方式。它是把变量实际的类型简单的罗列在变量名之前。以C语言为例,如变量iCount中的i表示这个变量的类型是语言内置的int型,stFooTmp的类型是用户自定义的结构体Foo。这些类型因为是实际的类型,又称之为物理类型。
SHN犹如符在变量身上的咒语。好处在于我们随时随地都可以透过这段奇异的符文看到变量的类型。在以前IDE尚显稚嫩的时代,倒是一用无妨。但是到了如今IDE足够智能之时,SHN就有些时过境迁了。借助IDE的魔法,我们也能随时随地知道变量的类型,甚至更多,比如变量是在哪里定义的,还能提供一些实时的错误检查。
对比之下,SHN更像是一种没有约束力的提示,就像注释一样。就算变量名里面的类型信息跟实际不符,编译器依然是安静的那一个,这时只能依靠人肉检查器,我想没几个人愿意成为肉盾。毕竟这是编译器的领域。琐碎的类型检查交给工具去做不是更好?编译器不会眼花,还能给我们更多的提示。
从设计上来看,SHN并没有提供有益的抽象,只是把类型平铺直叙的列举出来,有时还十分冗长和拗口。例如,下面这个摘自viki上的变量说明:
a_crszkvc30LastNameCol:
a constant reference argument, holding the contentsof a database column LastName of type varchar(30)which is part of the table's primary key.
这个例子中变量的含义淹没在类型中。如果C++模板用上SHN了,变量名该有多长。细思极恐。。。KISS(Keep It Simple Stupid)原则看来是满足不了了。
再看看下面这个赋值语句能不能赋值?
ulFoo = ulBar;
单单从变量名上,这两个变量的物理类型是一样的。但是从概念上是不是同一类的就不得而知了。
再则,SHN违反了SRP(Single Responsibility Principle)原则。变量名聚合了两个不怎么相关又不在同一层次的东西,变量的含义和类型。变量含义在概念层次上,而变量类型是在实现层上。思维在两个层次上来回跳转,一会上天,一会入地,最后头昏眼花。于是我基本上已经养成忽略变量前缀的习惯,一如忽略注释的习惯。我之所以如此,还有因为HN本身的免责声明:本变量名按现状提供,不提供变量名同步于变量类型之保证。自然时不时会遇到变量名跟变量类型不相符的情况。修改变量类型时,还得同步修改变量名,而且不只一处!于是DRY(Don't Repeat Yourself)原则也被打破了。
对SHN依赖过甚,部分是由于变量定义和使用隔得太远导致的。换言之,函数过长。如果函数动辄上百行,再加上C89要求变量定义必须放在代码块开头(再加上有的同学把代码块开头误解为函数开头),于是乎,变量的定义和使用不在一屏里面。就只能借助额外的手段来查变量类型了。如果函数很短,比如十行,还需要这样煞费苦心吗?
相比SHN,AHN则做了一层抽象,放在变量名前面不是物理类型,而是逻辑类型。例如如下代码:
int rwWidth, colWidth;
……
rwWidth = colWidth;
rw表示行(row),col表示列(column)。行宽跟列宽虽然物理类型是一样的,但是逻辑类型不一样(一个是行,一个是列)。把列赋给行,跨越了逻辑类型,可能存在问题。
从上可以看出,AHN构建了一个薄层,将物理类型转换为逻辑类型。逻辑类型将对象分门别类,紧随其后的名字有时候像是个属性。一般来说,逻辑类型跟名字要像X轴和Y轴一样尽量正交。
AHN的问题在于这一层只对人有意义,编译器不在乎,运行时也不关注。当然,这也有好处--几乎没有编译和运行的开销。但是坏处也显而易见,没有编译器的支持,约束力有限,就算将两个不同概念的变量赋值也能编译通过,又得人肉编译器出马了。。。Coder表示不开心,压力山大。
到了面向对象的时代,逻辑类型的约束可以通过自定义的类来展现出来。比如以前例子中的行和列,可以分别定义一个Row和Column类,重载算术操作符,再禁止Row和Column类相互的赋值。这种约束方式既可以借助编译器的检查,也能通过代码显式的表达出来。
搞清楚匈牙利命名法之后,下一个问题是怎么破冰?让我们先看看Linus是怎么号召大家清理注释的呢?他在邮件最后说道:So just get rid of the(no-no) and (no-no-no) forms. Not in one big go, but as people touch the code,just fix that mess up.
同样的,我们也不需要发起一次大扫荡,而是按需重构。那些本来就很稳定而又没有新需求的代码可以保持不变。新增文件和函数就按新规范来,不要再用匈牙利命名法了。至于修改已有代码,建议顺手重构。慢慢的,代码就变得更简洁了。
后记
最近听闻又冒出一个有望取代C++的新语言Rust。好吧,我只想说:C++ will never die。"又"字说明了一切。再加上C++11的进化步幅是如此之大。说话回来,Rust的名字由来众说纷纭而十分有趣。其中一个版本的解释来源于其字面意思--铁锈。话说一直以来编程语言领域有许多"老旧"的技术本是某些问题的灵丹妙药,却止步于实验室,随即束之高阁,无人问津。例如最近火起来的两种通用并发模型--actor模型和CSP(Communicating Sequential Process)模型,本是上个世纪70年代的老古董。值得一提的是CSP是由Tony Hoare提出的,Tony的一句名言对我影响很大,详见附录。所以,Rust不引入闪闪发亮的新技术,而是将生锈的老技术付诸实践,化腐朽为神奇。我们也莫不如是,三分热情的关心最新出炉的黑科技,以期找到银弹,结果却陷入死循环当中。高手之路看似高冷,其实一方面是打破已有的知识结构,学习吸收新的编程范式;另一方面把知识化整为零(就像迭代开发一样),不断的重复练习小小的“零”。我们如果把已有的优秀编码方式和习惯真正践行起来,效果将是巨大的。借用《周易》上的一句话:那将是“积善之家,必有余庆”。
附录:
- Linux咆哮邮件,里面他列举了正确和错误的注释方法: http://lkml.iu.edu/hypermail/linux/kernel/1607.1/00627.html
- 微软.net框架的命名约定: https://msdn.microsoft.com/zh-cn/library/ms229045.aspx
- Joel Spolsky的《软件随想录》第十三章“让错误的代码显而易见”对于匈牙利命名法有更详细的介绍
- Tony Hoare的名言:构建软件有两种方式:一种是使其足够简单,以至于明显没有错误;另外一种是使其非常复杂,以至于没有明显的错误。原文为:There are two ways of constructing a piece of software: One is to make it so simple that there are obviously no errors, and the other is to make it so complicated that there are no obvious errors.
我眼中的匈牙利命名法相关推荐
- 骆驼命名法,帕斯卡命名法和匈牙利命名法(转)
一.匈牙利命名法:广泛应用于象Microsoft Windows这样的环境中. Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Micros ...
- 【转】匈牙利命名法(Hungarian Notation)
http://www.hudong.com/wiki/%E5%8C%88%E7%89%99%E5%88%A9%E5%91%BD%E5%90%8D%E6%B3%95 匈牙利命名法 匈牙利命名法是一种编程 ...
- c++ 请抛弃匈牙利命名法 - 变量命名代码风格的建议。
我只针对c++码农们讲,其他语言不了解不过应该大同小异.曾几何时翻开21天学通c++系列等脑残入门书,都以匈牙利命名法示人(DWORD dwXXX, int nXXX, string strXXX). ...
- C++编程(一):匈牙利命名法
匈牙利命名法 许多 Windows 程序员都使用"匈牙利标记法"作为变量命名约定.这是为了纪念具有传奇色彩的微软程序员 Charles Simonyi.这种标记法非常简单,其基本原 ...
- mfc编程淘汰了吗_四种基本的编程命名规范(匈牙利命名法、驼峰式命名法、帕斯卡命名法、下划线命名法)...
匈牙利命名法 匈牙利命名法是早期的规范,由微软的一个匈牙利人发明的,是 IDE 还十分智障的年代的产物.那个年代,当代码量很多的时候,想要确定一个变量的类型是很麻烦的,不像现在 IDE 都会给提示,所 ...
- 匈牙利命名法、骆驼命名法、帕斯卡(pascal)命名法 C#命名规范
一.匈牙利命名法:广泛应用于象Microsoft Windows这样的环境中. Windows 编程中用到的变量(还包括宏)的命名规则匈牙利命名法,这种命名技术是由一位能干的 Microsoft 程序 ...
- [摘抄] 匈牙利命名法
匈牙利命名法中常用的小写字母的前缀: 前缀 整数类型 i 整型int l 长整型long int n 短整型short int w Word dw Double Word h Handle(句柄本身其 ...
- PHP书写规范 匈牙利命名法+驼峰法命名
2019独角兽企业重金招聘Python工程师标准>>> PHP书写规范 PHP Coding Standard 变量命名规范这里感觉 打算采用 匈牙利命名法+驼峰法命名,因为 PHP ...
- 匈牙利命名法鼻祖---查尔斯·西蒙尼
生平简介 1948年9月10日,查尔斯•西蒙尼(Charles Simonyi)出生于匈牙利布达佩斯.上高中时,他开始接触计算机和编程,父亲安排他给一名从事计算机工作的工程师当助手,当时计算机在匈牙利 ...
最新文章
- 如何在Spring Boot中玩转智能合约【修订版】
- 20165301第十周课下补做
- Django多进程中的查询错乱问题以及mysql gone away问题
- 【剑指offer】二叉树的深度_solution2
- python 全文搜索 句子_python新玩法:用python进行文章摘要拿取,只需要一行代码
- Java开发者必备:超全的Java问题排查工具单
- 端口冲突,可爱的8080
- VUE小需求——旋转小图标
- 3个月测试员自述:4个影响我职业生涯的重要技能
- fileReader学习-前端展示本地图片
- 内网服务器(不通外网)访问高德在线地图服务的方法
- Mysql组复制(MGR)——前提及限制
- MobaXterm连接虚拟机Ubuntu
- 小米球ngrok 给你惊喜
- 一切都是最好的安排之hibernate00
- 语音信号的同态处理、倒谱分析和Mel频率倒谱系数
- python批量循环图片识别_批量识别图中文字自动命名,让你1秒找到骚图
- unity 照片墙 流动 排斥 引力
- RegexBuddy、RegexMagic、EditPad官方绿色版
- 蚂蚁金服天街:蚂蚁双11大促OceanBase核心技术全解析