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


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

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


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

为了造福大众,在Java编程领域创造就业机会,兄弟我在此传授大师们的秘籍。这些大师写的代码极其难以维护,后继者就是想对它做最简单的修改都需要花上数年时间。而且,如果你能对照秘籍潜心修炼,你甚至可以给自己弄个铁饭碗,因为除了你之外,没人能维护你写的代码。再而且,如果你能练就秘籍中的全部招式,那么连你自己都无法维护你的代码了!

你不想练功过度走火入魔吧。那就不要让你的代码一眼看去就完全无法维护,只要它实质上是那样就行了。否则,你的代码就有被重写或重构的风险!


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

想挫败维护代码的程序员,你必须先明白他的思维方式。他接手了你的庞大程序,没有时间把它全部读一遍,更别说理解它了。他无非是想快速找到修改代码的位置、改代码、编译,然后就能交差,并希望他的修改不会出现意外的副作用。

他查看你的代码不过是管中窥豹,一次只能看到一小段而已。你要确保他永远看不到全貌。要尽量和让他难以找到他想找的代码。但更重要的是,要让他不能有把握忽略任何东西。

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

你可能会觉得每个语言特性都可以用来让代码难以维护,其实不然。你必须精心地误用它们才行。


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

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

  1. 妙用 宝宝起名大全

    买本宝宝起名大全,你就永远不缺变量名了。比如 

    单字母变量名

    创造性的拼写错误

    如果你必须使用描述性的变量和函数名,那就把它们都拼错。还可以把某些函数和变量名拼错,再把其他的拼对(例如 SetPintleOpening 和 SetPintalClosing) ,我们就能有效地将grep或IDE搜索技术玩弄于股掌之上。这招超级管用。还可以混淆不同语言(比如colour — 英国英语,和 color — 美国英语)。

    抽象

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

    首字母大写的缩写

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

    辞典大轮换

    为了打破沉闷的编程气氛,你可以用一本辞典来查找尽量多的同义词。例如 display, show, present。在注释里含糊其辞地暗示这些命名之间有细微的差别,其实根本没有。不过,如果有两个命名相似的函数真的有重大差别,那倒是一定要确保它们用相同的单词来命名(例如,对于 “写入文件”, “在纸上书写” 和 “屏幕显示” 都用 print 来命名)。 在任何情况下都不要屈服于编写明确的项目词汇表这种无理要求。你可以辩解说,这种要求是一种不专业的行为,它违反了结构化设计的信息隐藏原则。

    首字母大写

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

    重用命名

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

    使用非英语字母

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

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

      如果编译器只区分命名的前几位,比如前8位,那么就把后面的字母写得不一样。比如,其实是同一个变量,有时候写成 var_unit_update() ,有时候又写成 var_unit_setup(),看起来是两个不同的函数调用。而在编译的时候,它们其实是同一个变量 var_unit。

      下划线,一位真正的朋友

      可以拿 _ 和 __ 作为标示符。

      混合多语言

      随机地混用两种语言(人类语言或计算机语言都行)。如果老板要求使用他指定的语言,你就告诉他你用自己的语言更有利于组织你的思路,万一这招不管用,就去控诉这是语言歧视,并威胁起诉老板要求巨额精神损失赔偿。

      扩展 ASCII 字符

      其他语言的命名

      数学命名

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

        = (slash  + asterix)  / equals;
        (左圆括 = (斜杠 + 星 )/等 ;)
  2. 令人眩晕的命名

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

    = (superman  + starship)  / god;
    (欢乐满人间 = (超人 + 星河战队)/上帝;)

这一招可以让阅读代码的人陷入迷惑之中,因为他们在试图想清楚这些命名的逻辑时,会不自觉地联系到不同的感情场景里而无法自拔。

何时使用 i

永远不要把 

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

忽视 Java 编码惯例,Sun 就是这样做的。幸运的是,你违反了它编译器也不会打小 告。这一招的目的是搞出一些在某些特殊情况下有细微差别的名字来。如果你被强迫遵循驼峰法命名,你还是可以在某些模棱两可的情况下颠覆它。例如,input File name 和 input file Name 两个命名都可以合法使用。在此基础上自己发明一套复杂到变态的命名惯例,然后就可以痛扁其他人,说他们违反了惯例。

小写的 l 看上去很像数字 1

用小写字母 l 标识 long 常数。例如 10l 更容易被误认为是 101 而不是 10L 。 禁用所有能让人准确区分 uvw wW gq9 2z 5s il17|!j oO08 `'” ;,. m nn rn {[()]} 的字体。要做个有创造力的人。

把全局命名重用为私有

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

误导性的命名

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


    1. 把代码伪装成注释,反之亦然

      下面包括了一些被注释掉的代码,但是一眼看去却像是正常代码。

        <array_len; j + =8)

        • total  += array [j +]
          total  += array [j +]
          total  += array [j +]/* Main body of 
          total += array[j+3]; * loop is unrolled 
          total += array[j+4]; * for greater speed. 
          total += array[j+5]; */
           
          total  += array [j +]
          total  += array [j +]
          }
    2. 如果不是用绿色标出来,你能注意到这三行代码被注释掉了么


    1. 用连接符隐藏变量

      对于下面的定义

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


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

    上一篇 2014年11月12日
    下一篇 2014年11月13日

    相关推荐