资深程序猿冒死揭开软件潜规则:无法维护的代码


原始博文公布于: Roedy Green’s Mindproducts (http://mindprod.com/unmain.html )。

 翻译链接: 点击打开链接 2014年11月25日 03:11

怎样编写无法维护的代码

让自己稳拿铁饭碗 ;-)

Roedy Green

简单介绍

永远不要(把自己遇到的问题)归因于(他人的)恶意,这恰恰说明了(你自己的)无能。

 — 拿破仑

为了造福大众,在Java编程领域创造就业机会,兄弟我在此传授大师们的秘籍。这些大师写的代码极其难以维护。后继者就是想对它做最简单的改动都须要花上数年时间。并且,假设你能对比秘籍潜心修炼,你甚至能够给自己弄个铁饭碗。由于除了你之外,没人能维护你写的代码。

再并且。假设你能练就秘籍中的所有招式,那么连你自己都无法维护你的代码了!

你不想练功过度走火入魔吧。

那就不要让你的代码一眼看去就全然无法维护,仅仅要它实质上是那样即可了。否则。你的代码就有被重写或重构的风险。

整体原则

Quidquid latine dictum sit, altum sonatur. 
(随便用拉丁文写点啥都会显得高大上。)

想挫败维护代码的程序猿,你必须先明确他的思维方式。

他接手了你的庞大程序,没有时间把它所有读一遍,更别说理解它了。他无非是想高速找到改动代码的位置、改代码、编译,然后就能交差,并希望他的改动不会出现意外的副作用。

他查看你的代码只是是管中窥豹,一次仅仅能看到一小段而已。

你要确保他永远看不到全貌。要尽量和让他难以找到他想找的代码。但更重要的是。要让他不能有把握忽略不论什么东西。

程序猿都被编程惯例洗脑了,还为此自鸣得意。每一次你处心积虑地违背编程惯例,都会迫使他必须用放大镜去细致阅读你的每一行代码。

你可能会认为每一个语言特性都能够用来让代码难以维护。事实上不然。你必须精心地误用它们才行。

命名

“当我使用一个单词的时候” Humpty Dumpty 以前用一种轻视的口气说, “它就是我想表达的意思。不多也不少。“ 
– Lewis Carroll — 《爱丽丝魔镜之旅》, 第6章

编写无法维护代码的技巧的重中之重是变量和方法命名的艺术。怎样命名是和编译器无关的。这就让你有巨大的自由度去利用它们迷惑维护代码的程序猿。

妙用 宝宝起名大全

买本宝宝起名大全。你就永远不缺变量名了。比方  Fred 就是个好名字。并且键盘输入它也省事。假设你就想找一些easy输入的变量名,能够试试  adsf 或者  aoeu之类。

单字母变量名

并且,没人能猜到它们的含义。

创造性的拼写错误

假设你必须使用描写叙述性的变量和函数名。那就把它们都拼错。

还能够把某些函数和变量名拼错,再把其它的拼对(比如 SetPintleOpening 和 SetPintalClosing) ,我们就能有效地将grep或IDE搜索技术玩弄于股掌之上。

这招超级管用。还能够混淆不同语言(比方colour — 英国英语,和 color — 美国英语)。

抽象

在命名函数和变量的时候,充分利用抽象单词,比如 it, everything, data, handle, stuff, do, routine, perform 和数字。比如 e.g.  routineX48PerformDataFunctionDoItHandleStuff还有  do_args_method

首字母大写的缩写

用首字母大写缩写(比方GNU 代表 GNU’s Not Unix) 使代码简洁难懂。真正的汉子(不管男女)从来不说明这样的缩写的含义,他们生下来就懂。

辞典大轮换

为了打破沉闷的编程气氛。你能够用一本辞典来查找尽量多的同义词。比如 display, show, present。在凝视里含糊其辞地暗示这些命名之间有细微的区别,事实上根本没有。

只是,假设有两个命名相似的函数真的有重大区别。那倒是一定要确保它们用同样的单词来命名(比如。对于 “写入文件”, “在纸上书写” 和 “屏幕显示” 都用 print 来命名)。

在不论什么情况下都不要屈服于编写明白的项目词汇表这样的无理要求。你能够辩讲解,这样的要求是一种不专业的行为,它违反了结构化设计的信息隐藏原则。

首字母大写

随机地把单词中间某个音节的首字母大写。比如  ComputeReSult()

重用命名

在语言规则同意的地方,尽量把类、构造器、方法、成员变量、參数和局部变量都命名成一样。更高级的技巧是在{}块中重用局部变量。这样做的目的是迫使维护代码的程序猿认真检查每一个演示样例的范围。特别是在Java代码中,能够把普通方法伪装成构造器。

使用非英语字母

在命名中偷偷使用不易察觉的非英语字母,比如

  • typedef struct { int i; } ínt;

巧妙利用编译器对于命名长度的限制

假设编译器仅仅区分命名的前几位,比方前8位,那么就把后面的字母写得不一样。

比方。事实上是同一个变量,有时候写成 var_unit_update() ,有时候又写成 var_unit_setup(),看起来是两个不同的函数调用。而在编译的时候,它们事实上是同一个变量 var_unit。

下划线。一位真正的朋友

能够拿 _ 和 __ 作为标示符。

混合多语言

随机地混用两种语言(人类语言或计算机语言都行)。

假设老板要求使用他指定的语言。你就告诉他你用自己的语言更有利于组织你的思路,万一这招无论用,就去控诉这是语言鄙视,并威胁起诉老板要求巨额精神损失赔偿。

扩展 ASCII 字符

其它语言的命名

比如,能够用德语单词 punkt 取代 point。

除非维护代码的程序猿也像你一样熟练掌握了德语. 不然他就仅仅能尽情地在代码中享受异域风情了。

数学命名

用数学操作符的单词来命名变量。比如:

  • openParen 

= (slash  + asterix)  / equals;
(左圆括 = (斜杠 + 星 )/等 ;)

令人眩晕的命名

用带有全然不相关的感情色彩的单词来命名变量。比如:

  • marypoppins 

= (superman  + starship)  / god;
(欢乐满人间 = (超人 + 星河战队)/上帝;) 这一招能够让阅读代码的人陷入迷惑之中,由于他们在试图想清楚这些命名的逻辑时。会不自觉地联系到不同的感情场景里而无法自拔。

何时使用 i

永远不要把  i 用作最内层的循环变量。

用什么命名都行,就是别用i。把 i 用在其它地方就随便了,用作非整数变量尤其好。

惯例 — 明修栈道,暗度陈仓

忽视 Java 编码惯例。Sun 就是这样做的。幸运的是,你违反了它编译器也不会打小 告。这一招的目的是搞出一些在某些特殊情况下有细微区别的名字来。假设你被强迫遵循驼峰法命名。你还是能够在某些模棱两可的情况下颠覆它。

比如。inputFilename 和 inputfileName 两个命名都能够合法使用。在此基础上自己发明一套复杂到变态的命名惯例,然后就能够痛扁其它人,说他们违反了惯例。

小写的 l 看上去非常像数字 1

用小写字母 l 标识 long 常数。比如 10l 更easy被误觉得是 101 而不是 10L 。

禁用全部能让人准确区分 uvw wW gq9 2z 5s il17|!j oO08 `'” ;,. m nn rn {[()]} 的字体。要做个有创造力的人。

把全局命名重用为私有

在A 模块里声明一个全局数组,然后在B 模块的头文件中在声明一个同名的私有数组,这样看起来你在B 模块里引用的是那个全局数组,事实上却不是。不要在凝视里提到这个反复的情况。

误导性的命名

让每一个方法都和它的名字蕴含的功能有一些差异。

比如。一个叫 isValid(x)的方法在推断完參数x的合法性之后,还顺带着把它转换成二进制并保存到数据库里。

伪装

把代码伪装成凝视,反之亦然

以下包含了一些被凝视掉的代码,可是一眼看去却像是正常代码。

  • for(j

=0; j <array_len; j + =8)


  • total 

+= array [j +0  ]; 
total  += array [j +1  ]; 
total  += array [j +2  ];  /* Main body of 
total += array[j+3]; * loop is unrolled 
total += array[j+4]; * for greater speed. 
total += array[j+5]; */
 
total  += array [j +6  ]; 
total  += array [j +7  ]; 
} 假设不是用绿色标出来。你能注意到这三行代码被凝视掉了么

用连接符隐藏变量

对于以下的定义

  • #define local_var xy_z

能够把 “xy_z” 打散到两行里:

  • #define local_var xy 
    _z // local_var OK

这样全局搜索 xy_z 的操作在这个文件中就一无所获了。 对于 C 预处理器来说。第一行最后的 “” 表示继续拼接下一行的内容。

  • 声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

  • 上一篇 2017年4月1日
    下一篇 2017年4月1日

    相关推荐