[转载]如何编写无法维护的代码(2)

        这是一篇转载文章,最原始的是英文,这是英文原文的地址,然后由伯乐在线的老码农翻译,这是翻译原文的地址,由于翻译的原文很长,故在转载时拆分成为多篇。以下为转载的翻译:

(接前一篇)

文档

任何傻瓜都能说真话,而要把谎编圆则需要相当的智慧。- Samuel Butler (1835 – 1902)

不正确的文档往往比没有文档还糟糕。- Bertrand Meyer

        既然计算机是忽略注释和文档的,你就可以在里边堂而皇之地编织弥天大谎,让可怜的维护代码的程序员彻底迷失。

在注释中撒谎

        实际上你不需要主动地撒谎,只要没有及时保持注释和代码更新的一致性就可以了。

只记录显而易见的东西

        往代码里掺进去类似于

/* 给 i 加 1 */

        这样的注释,但是永远不要记录包或者方法的整体设计这样的干货。

记录 How 而不是 Why

        只解释一个程序功能的细节,而不是它要完成的任务是什么。这样的话,如果出现了一个bug,修复者就搞不清这里的代码应有的功能。

该写的别写

        比如你在开发一套航班预定系统,那就要精心设计,让它在增加另一个航空公司的时候至少有25处代码需要修改。永远不要在文档里说明要修改的位置。后来的开发人员要想修改你的代码?门都没有,除非他们能把每一行代码都读懂。

计量单位

        永远不要在文档中说明任何变量、输入、输出或参数的计量单位,如英尺、米、加仑等。计量单位对数豆子不是太重要,但在工程领域就相当重要了。同 理,永远不要说明任何转换常量的计量单位,或者是它的取值如何获得。要想让代码更乱的话,你还可以在注释里写上错误的计量单位,这是赤裸裸的欺骗,但是非 常有效。如果你想做一个恶贯满盈的人,不妨自己发明一套计量单位,用自己或某个小人物的名字命名这套计量单位,但不要给出定义。万一有人挑刺儿,你就告诉 他们,你这么做是为了把浮点数运算凑成整数运算而进行的转换。

        永远不要记录代码中的坑。如果你怀疑某个类里可能有bug,天知地知你知就好。如果你想到了重构或重写代码的思路,看在老天爷的份上,千万别写出 来。切记电影《小鹿斑比》里那句台词 “如果你不能说好听的话,那就什么也不要说。”。万一这段代码的原作者看到你的注释怎么办?万一老板看到了怎么办?万一客户看到了怎么办?搞不好最后你自 己被解雇了。一句”这里需要修改“的匿名注释就好多了,尤其是当看不清这句注释指的是哪里需要修改的情况下。切记“难得糊涂”四个字,这样大家都不会感觉 受到了批评。

说明变量

        永远不要对变量声明加注释。有关变量使用的方式、边界值、合法值、小数点后的位数、计量单位、显示格式、数据录入规则等等,后继者完全可以自己从程序代码中去理解和整理嘛。如果老板强迫你写注释,就在方法体里胡乱多写点,但绝对不要对变量声明写注释,即使是临时变量!

在注释里挑拨离间

        为了阻挠任何雇佣外部维护承包商的倾向,可以在代码中散布针对其他同行软件公司的攻击和抹黑,特别是可能接替你工作的其中任何一家。例如:

/* 优化后的内层循环
这套技巧对于SSI软件服务公司的那帮蠢材来说太高深了,他们只会
用 <math.h> 里的笨例程,消耗50倍的内存和处理时间。
*/
class clever_SSInc
{ 
.. . 
}

        可能的话,除了注释之外,这些攻击抹黑的内容也要掺到代码里的重要语义部分,这样如果管理层想清理掉这些攻击性的言论然后发给外部承包商去维护,就会破坏代码结构。

程序设计

编写无法维护代码的基本规则就是:在尽可能多的地方,以尽可能多的方式表述每一个事实。- Roedy Green

        编写可维护代码的关键因素是只在一个地方表述应用里的一个事实。如果你的想法变了,你也只在一个地方修改,这样就能保证整个程序正常工作。所以, 编写无法维护代码的关键因素就是反复地表述同一个事实,在尽可能多的地方,以尽可能多的方式进行。令人高兴的是,像Java这样的语言让编写这种无法维护 代码变得非常容易。例如,改变一个被引用很多的变量的类型几乎是不可能的,因为所有造型和转换功能都会出错,而且关联的临时变量的类型也不合适了。而且, 如果变量值要在屏幕上显示,那么所有相关的显示和数据录入代码都必须一一找到并手工进行修改。类似的还有很多,比如由C和Java组成的Algol语言系 列,Abundance甚至Smalltalk对于数组等结构的处理,都是大有可为的。

Java 造型

        Java的造型机制是上帝的礼物。你可以问心无愧地使用它,因为Java语言本身就需要它。每次你从一个Collection 里获取一个对象,你都必须把它造型为原始类型。这样这个变量的类型就必须在无数地方表述。如果后来类型变了,所有的造型都要修改才能匹配。如果倒霉的维护 代码的程序员没有找全(或者修改太多),编译器能不能检测到也不好说。类似的,如果变量类型从short 变成 int,所有匹配的造型也都要从(short) 改成 (int)。

利用Java的冗余

        Java要求你给每个变量的类型写两次表述。 Java 程序员已经习惯了这种冗余,他们不会注意到你的两次表述有细微的差别,例如

Bubblegum b = new Bubblegom();

不幸的是 ++ 操作符的盛行让下面这种伪冗余代码得手的难度变大了:

swimmer = swimner + 1;

永远不做校验

        永远不要对输入数据做任何的正确性或差异性检查。这样能表现你对公司设备的绝对信任,以及你是一位信任所有项目伙伴和系统管理员的团队合作者。总是返回合理的值,即使数据输入有问题或者错误。

有礼貌,无断言

        避免使用 assert() 机制,因为它可能把三天的debug盛宴变成10分钟的快餐。

避免封装

        为了提高效率,不要使用封装。方法的调用者需要所有能得到的外部信息,以便了解方法的内部是如何工作的。

复制粘贴修改

        以效率的名义,使用 复制+粘贴+修改。这样比写成小型可复用模块效率高得多。在用代码行数衡量你的进度的小作坊里,这招尤其管用。

使用静态数组

        如果一个库里的模块需要一个数组来存放图片,就定义一个静态数组。没人会有比512 X 512 更大的图片,所以固定大小的数组就可以了。为了最佳精度,就把它定义成 double 类型的数组。

傻瓜接口

        编写一个名为 “WrittenByMe” 之类的空接口,然后让你的所有类都实现它。然后给所有你用到的Java 内置类编写包装类。这里的思想是确保你程序里的每个对象都实现这个接口。最后,编写所有的方法,让它们的参数和返回类型都是这个 WrittenByMe。这样就几乎不可能搞清楚某个方法的功能是什么,并且所有类型都需要好玩的造型方法。更出格的玩法是,让每个团队成员编写它们自己 的接口(例如 WrittenByJoe),程序员用到的任何类都要实现他自己的接口。这样你就可以在大量无意义接口中随便找一个来引用对象了。

巨型监听器

        永远不要为每个组件创建分开的监听器。对所有按钮总是用同一个监听器,只要用大量的if…else 来判断是哪一个按钮被点击就行了。

好事成堆TM

狂野地使用封装和OO思想。例如

myPanel.add( getMyButton() ); 
private JButton getMyButton()
{ 
     return myButton; 
}

        这段很可能看起来不怎么好笑。别担心,只是时候未到而已。

友好的朋友

        在C++ 里尽量多使用friend声明。再把创建类的指针传递给已创建类。现在你不用浪费时间去考虑接口了。另外,你应该用上关键字private 和 protected 来表明你的类封装得很好。

使用三维数组

        大量使用它们。用扭曲的方式在数组之间移动数据,比如,用arrayA里的行去填充arrayB的列。这么做的时候,不管三七二十一再加上1的偏移值,这样很灵。让维护代码的程序员抓狂去吧。

混合与匹配

        存取方法和公共变量神马的都要给他用上。这样的话,你无需调用存取器的开销就可以修改一个对象的变量,还能宣称这个类是个”Java Bean”。对于那些试图添加日志函数来找出改变值的源头的维护代码的程序员,用这一招来迷惑他尤其有效。

没有秘密!

        把每个方法和变量都声明为 public。毕竟某个人某天可能会需要用到它。一旦方法被声明为public 了,就很难缩回去。对不?这样任何它覆盖到的代码都很难修改了。它还有个令人愉快的副作用,就是让你看不清类的作用是什么。如果老板质问你是不是疯了,你 就告诉他你遵循的是经典的透明接口原则。

全堆一块

        把你所有的没用的和过时的方法和变量都留在代码里。毕竟说起来,既然你在1976年用过一次,谁知道你啥时候会需要再用到呢?当然程序是改了,但 它也可能会改回来嘛,你”不想要重新发明轮子”(领导们都会喜欢这样的口气)。如果你还原封不动地留着这些方法和变量的注释,而且注释写得又高深莫测,甭 管维护代码的是谁,恐怕都不敢对它轻举妄动。

就是 Final

        把你所有的叶子类都声明为 final。毕竟说起来,你在项目里的活儿都干完了,显然不会有其他人会通过扩展你的类来改进你的代码。这种情况甚至可能有安全漏洞。 java.lang.String 被定义成 final 也许就是这个原因吧?如果项目组其他程序员有意见,告诉他们这样做能够提高运行速度。

避免布局

        永远不要用到布局。当维护代码的程序员想增加一个字段,他必须手工调整屏幕上显示所有内容的绝对坐标值。如果老板强迫你使用布局,那就写一个巨型的 GridBagLayout 并在里面用绝对坐标进行硬编码。

全局变量,怎么强调都不过分

        如果上帝不愿意我们使用全局变量,他就不会发明出这个东西。不要让上帝失望,尽量多使用全局变量。每个函数最起码都要使用和设置其中的两个,即使没有理由也要这么做。毕竟,任何优秀的维护代码的程序员都会很快搞清楚这是一种侦探工作测试,有利于让他们从笨蛋中脱颖而出。

再一次说说全局变量

        全局变量让你可以省去在函数里描述参数的麻烦。充分利用这一点。在全局变量中选那么几个来表示对其他全局变量进行操作的类型。

局部变量

        永远不要用局部变量。在你感觉想要用的时候,把它改成一个实例或者静态变量,并无私地和其他方法分享它。这样做的好处是,你以后在其他方法里写类似声明的时候会节省时间。C++程序员可以百尺竿头更进一步,把所有变量都弄成全局的。

配置文件

        配置文件通常是以 关键字 = 值 的形式出现。在加载时这些值被放入 Java 变量中。最明显的迷惑技术就是把有细微差别的名字用于关键字和Java 变量.甚至可以在配置文件里定义运行时根本不会改变的常量。参数文件变量和简单变量比,维护它的代码量起码是后者的5倍。

子类

        对于编写无法维护代码的任务来说,面向对象编程的思想简直是天赐之宝。如果你有一个类,里边有10个属性(成员/方法),可以考虑写一个基类,里 面只有一个属性,然后产生9层的子类,每层增加一个属性。等你访问到最终的子类时,你才能得到全部10个属性。如果可能,把每个类的声明都放在不同的文件 里。

编码迷局

迷惑 C

        从互联网上的各种混乱C 语言竞赛中学习,追随大师们的脚步。

追求极致

        总是追求用最迷惑的方式来做普通的任务。例如,要用数组来把整数转换为相应的字符串,可以这么做:

char *p; 
switch (n) 
{ 
case 1: 
    p = "one"; 
    if (0) 
case 2: 
    p = "two"; 
    if (0) 
case 3: 
    p = "three"; 
    printf("%s", p); 
    break; 
}

一致性的小淘气

        当你需要一个字符常量的时候,可以用多种不同格式: ‘ ‘, 32, 0×20, 040。在C或Java里10和010是不同的数(0开头的表示8进制),你也可以充分利用这个特性。

造型

        把所有数据都以 void * 形式传递,然后再造型为合适的结构。不用结构而是通过位移字节数来造型也很好玩。

嵌套 Switch

        Switch 里边还有 Switch,这种嵌套方式是人类大脑难以破解的。

利用隐式转化

        牢记编程语言中所有的隐式转化细节。充分利用它们。数组的索引要用浮点变量,循环计数器用字符,对数字执行字符串函数调用。不管怎么说,所有这些 操作都是合法的,它们无非是让源代码更简洁而已。任何尝试理解它们的维护者都会对你感激不尽,因为他们必须阅读和学习整个关于隐式数据类型转化的章节,而 这个章节很可能是他们来维护你的代码之前完全忽略了的。

分号!

        在所有语法允许的地方都加上分号,例如:

if(a); 
else;
{ 
int d; 
d = c; 
} 
;

使用八进制数

        把八进制数混到十进制数列表里,就像这样:

array = new int []
{ 
111, 
120, 
013,
121, 
};

嵌套

        尽可能深地嵌套。优秀的程序员能在一行代码里写10层(),在一个方法里写20层{}。

C数组

        C编译器会把 myArray[i] 转换成 *(myArray + i),它等同于 *(i + myArray) 也等同于 i[myArray]。 高手都知道怎么用好这个招。可以用下面的函数来产生索引,这样就把代码搞乱了:

int myfunc(int q, int p) { return p%q; } 
... 
myfunc(6291, 8)[Array];

遗憾的是,这一招只能在本地C类里用,Java 还不行。

放长线钓大鱼

        一行代码里堆的东西越多越好。这样可以省下临时变量的开销,去掉换行和空格还可以缩短源文件大小。记住,要去掉运算符两边的空格。优秀的程序员总是能突破某些编辑器对于255个字符行宽的限制。

异常

        在这里我要向你传授一个编程领域里鲜为人知的秘诀。异常是个讨厌的东西。良好的代码永远不会出错,所以异常实际上是不必要的。不要把时间浪费在这 上面。子类异常是给那些知道自己代码会出错的低能儿用的。在整个应用里,你只用在main()里放一个try/catch,里边直接调用 System.exit()就行了。在每个方法头要贴上标准的抛出集合定义,至于会不会抛出异常你就甭管了。

使用异常的时机

        在非异常条件下才要使用异常。比如终止循环就可以用 ArrayIndexOutOfBoundsException。还可以从异常里的方法返回标准的结果。

狂热奔放地使用线程

        如题。

(未完待续)

你可能感兴趣的:(编程规范,不可维护代码)