介绍永远不要把无能归咎于作恶 ------ 拿破仑 为了获得在Java编程领域工作的机会,我把这些来自于大师们关于如何编写难以维护代码的技巧传达给大家。如果你使用这些技巧,那些后来继承你工作的人即使做最简单的改动也要花费几年的时间。进一步,如果你能遵守所有的这些规则,你将能保证一辈子都不会被解雇,因为除了你已经没有人能维护这些操蛋的代码。甚至,如果你虔诚地遵守所有这些规则,连你自己也没办法维护这些代码!你怕不怕? 你不需要做的太过火了。你的代码不应该看起来毫无维护的可能性,就让它们那样。否则,它们可能面临着被重写或者重构的风险。 总则
为了打击维护代码的程序员,你应该了解他是怎么想的。他有你伟大的源代码。他没有时间全部读一遍,理解的部分就更少了。他希望快速找到哪里可以进行他的修改,加入他的代码,然后退出,并且不带来任何的副作用。 他从一个卫生纸卷筒中窥视你的代码。他一次只能看到一小片你的代码。你应该确保他这样做是不可能看到代码的宏图框架的。你应该让他难以找到他想要找的代码。更重要的是,你应该尽力让他难以安全地忽视任何细节。 程序员总是被约定麻痹。偶尔违反下约定,你就能强迫得他们用一个放大镜一行一行读你的代码。 可能你已经得到让所有编程语言变得不可维护的诀窍了。---- 除此之外,就是适当的滥用一下这技巧。 命名
编写不可维护代码的一大技巧就是命名变量和函数的艺术。命名对编译器没有意义。但是给了你迷惑代码维护程序员的巨大空间。 小孩名字的新用法买一本给小孩起名字的参考书,这样你在给变量命名时绝对不会吃亏。Fred是一个非常不错的名字,也很容易输入。如果你在找容易输入的变量名字,试试asdf或者如果你用的DSK排列的键盘,aoeu也不错。 单字母变量如果你给你的变量命名为a,b,c,那么别人不可能在简单的文本编辑器里搜索这些变量实例。甚至,没人能够猜测到这些变量是干什么的。如果有人喜欢打破从Fortran用i,j和k来用做遍历变量的传统,改成用ii,jj和kk,我要警告他们想想西班牙宗教法庭对异教徒们做了什么。 充满创意的错误拼写如果你必须描述性的变量和函数名,错误地拼写他们。有时错误有时正确的拼写(比如SetPintleOpening 和SetPintalClosing)我们可以高效的搞死grep命令或者IDE的搜索功能。这个非常的神奇。往不同的threater/theater添加国际范的tory或者tori拼写也是非常神奇的一件事。 抽象一点在命名函数和变量时,大量使用抽象的词语,比如everything,data,handle,stuff,do,routine,perform和数字等。例如,routineX48,PerformDataFunction,DoIt,HandlerStuff和do_args_method。 使用首字母缩写词使用首字母缩写词来保持代码简洁。真正的人类从来不定义首字母缩写词,他们生来就知道首字母缩写词的意义。 词汇替代为了打破无聊,用词典尽可能多地查询那些表示同一动作的词汇列表。比如,display,show,present。含糊地暗示这些动作词汇有细微的差别,但实际上根本没有。当然,如果真的有差别的函数,就用同一个词来描述(比如,用print同时表示“写文件”,“打印到纸上”和“显示在屏幕上”)。不管什么场景,都不要屈服于使用没有歧义的特殊用途的专有词汇。这样做非常不专业地违反了结构化设计准则中的信息隐藏原则。(译注:此处应该是调侃信息隐藏,信息隐藏实际应该指的是数据封装时尽可能少暴露内部信息) 使用其他语言的复数方式 用一个虚拟机脚本记录不同计算机(vaxes故意写成vaxen)数据(states故意写成statii)。世界通用语,克林贡语和霍比特语被认为是一种语言就是这么干的。对于伪世界语的复数形式,加上oj。你也可以为世界和平作出贡献。(译注:这一段有点难翻译,大意是按照自己的想法随意地加复数后缀,就像影视小说里面的异世界语言一样。这会让人很难理解) CapiTaliSation(字母大写) 随机大写单词中的音节字母。比如, 重用名字 只要语法规则允许,就给类,构造函数,函数,变量,参数和局部变量起相同的名字。另外,给{}中的局部变量起相同的名字。目的就是让维护者不得不仔细地检查每个实例的作用域。特别对于Java,把普通函数伪装成构造函数。 使用重音字母 在变量名里使用重音符号,比如
这里第二个int的i实际上是重音的i。在简单的文本编辑器里,几乎不可能分辨出这个倾斜的重音符号。 利用编译器能识别的变量名长度限制 例如假设编译器只识别前8个字符,那么var_unit_update和var_unit_setup都会被认为和var_unit一样。 下划线,其实是一个朋友 用 '_' 和 '__' 作标识符。 随机点缀使用两种语言(人类语言和计算机语言)。如果你的老板坚持你使用他的语言,你就告诉他使用你自己的语言你能更好地组织你的想法。或者如果那样说不好使,就说这是语言歧视,然后威胁说要控告他们索赔一大笔钱。 扩展ASCII字符 扩展ASCII字符完全是合法的变量字符,包括 ß, Ð, 和 ñ 字符。这些字符除了复制粘贴几乎不可能在一个简单的编辑器里编辑。 使用其他语言的名字 使用其他外国语言的字典作为变量起名的来源。例如,使用法语的punkt作为point。代码维护者没有你对法语的理解,将会非常享受这次多元文化秘密探索之旅。 用数学运算符号做名字 使用伪装成数学运算符的变量名,例如:
(译注:单词意思是‘左圆括号’ = (‘斜线’ + ‘星号’) / ‘等号’) 使用使人眼花缭乱的名字 选择那些有不相关情感内涵的词,例如:
(译注:欢乐满人间(电影名) = (超人 + 星际飞船)/ 上帝) 每个人对词汇的情感内涵都有不同的的理解,所以这样会让读代码的人很困惑。 重命名和重复使用 这个技巧在Ada语言中工作的尤其好。Ada是一种免除了现代模糊技术的语言。命名你现在使用的对象和包的那些人都是白痴。与其通知他们修改,不如按照自己的发明进行重命名和子类型重命名所有的名字。当然保留一些原来的名字,作为一个粗心的陷阱。 何时使用i 千万不要把i用作最里层的循环变量。公平地把i用在任何其他地方,尤其是非整型变量的地方。类似的,用n做循环变量。 约定 忽略Sun公司的Java编码约定。幸运的是,当你违反这些约定时编译器也不会泄露你的秘密。目标就是想出只在大小写有细微差别的的命名。如果不得不准守大小写约定,你依然可以在一些模糊定义的地方颠覆这些约定。例如,同时用inputFilename 和 inputfileName。发明一套你自己的复杂到令人绝望的名字约定,然后严厉地斥责那些不遵守这个约定的人。 小写的l很像数字1 用小写的l来标识long类型常数。例如10l 更容易被误解成101。不使用那些能清楚分辨 重用全局名字做为私有变量名 在模块A定义一个全局数组,在模块B的头文件定义一个同样名字的私有数组变量。这样似乎你在模块B用的是全局数组,但其实不是。不要在注释里提及这里的复用。 循环访问 用一种矛盾的方式尽可能迷惑地循环使用变量名作用域。例如,假设有全局变量A和B,和函数foo和bar。A一般传递给foo,B传递给bar。那么把foo函数定义成foo(B)和bar函数定义成bar(A)。这样在函数内部,A会被当成B,B别当成A。等有更多的函数和全局变量,你就可以生成一张互相矛盾地使用相同名字的变量组成的大网。 回收利用变量 只要作用域允许,重复利用已经存在的不相关的变量名。类似的,对于两个不相关的用途使用相同的临时变量(对外声称是节约栈空间)。就像恶魔变体一样对变量进行变化。例如,在一个很长的函数的前面给变量赋一个值,然后在中间某个地方再赋一个值,同时细微地改变变量的含义。比如,从以0为基准改成以1为基准。当然,不要把这个改变写进文档。 Cd wrttn wtht vwls s mch trsr(译注:乱七八糟的缩写)在变量和函数名里使用缩写时,对同一个单词用不同变体打破无聊,甚至偶尔速记一把。这帮助了打败那些使用文本搜索去理解你代码某个方面的懒汉。例如,混合国际方式拼写的colour和美国方式拼写color以及花花公子口中的kulerz。如果你全拼这些名字,只有一种拼写方式,这样太容易让代码维护者记住了。因为有很多不同的简写同一个单词的方式,使用简写你可以有很多不同的变量表示同一个表面上的意思。作为一个附加的福利,代码维护者可能甚至都没注意到他们是不同的变量。 误导人的命名 确保每个函数都比它的名字表达出的意思多做或者少做一点。举个简单的例子,一个名为isValid(x)的函数有个把x转成二进制且存进数据库的副作用。 m_ 一个来自C++的命名约定是在变量名前使用m_前缀。这个约定只要你“遗忘性地”在函数也加个m,就可以告诉他们这不是函数。 o_apple obj_apple 在每个类实例前加o或者obj前缀,以表明你想表示一个巨大的,多形态的宏图。 匈牙利命名法匈牙利命名法是代码混淆技术的战术核武器, 使用它! 让大量的源代码被这种习语污染,没有什么能比这种精心策划的匈牙利命名法攻击更快的干掉一名维护工程师了! 以下的提示将帮助你从本质上腐蚀匈牙利命名法 :
再说匈牙利命名法 一个匈牙利命名法中后续的招数是“改变变量的类型但是不改变变量名”。这几乎是从16位Windows时代一层不变地迁移来的:
到win32
这里的w都表明他们是单词,但实际上是指long类型。这种命名法迁移到win64时,意义会很明确,因为在win64就是参数的位宽就是64位。但是以前的w和l前缀,就呵呵了。 减少,重用,再利用 如果你为回调函数定义一个结构体来存储数据,把结构体命名为PRIVDATA。每个模块都可以定义它自己的PRIVDATA。在vc++里,这样做有个好处就是迷惑代码调试者。因为有很多PRIVATA展示在watch窗口,他不知道你指的是哪个PRIVATA,所以他只能任选一个。 晦涩地参考电影情节 用LancelotsFavouriteColour(译注:Lancelot最喜欢的颜色)替代blue给常量命名,并且赋值十六进制的$0204FB。这种颜色在屏幕上看起来和纯蓝色完全一样,但是这样命名会让代码维护者不得不执行程序(或者使用某些图像工具)才能知道是什么样。只有那些熟悉“巨蟒剧团”和《圣杯的故事》才知道Lancelot最喜欢的颜色是蓝色。如果代码维护者想不起来所有巨蟒剧团的电影,那他或者她就没资格做一个程序员。 伪装
编写不可维护代码一大部分技巧都是伪装的艺术。很多技巧都基于代码编译器能够比人眼和文本编辑器更好地分辨代码这一事实上。这里列举一些最佳伪装技术。 代码和注释相互伪装 来一些第一眼看上去是注释的代码。
如果没有编辑器颜色区分代码,你能注意到那三行已经别注释掉了吗? 命名空间struct/union 和 typedef struct/union 在C(不是C++)中是不同的命名空间。在这两个名字空间里给结构体和枚举使用相同的名字。如果可能的话,让他们几乎一模一样。
隐藏宏定义把宏定义隐藏在垃圾注释中间。程序员就会因为无聊放弃读这些注释,于是就发现不了这些宏。确保这些宏替换看起来像一个完全合法的赋值操作,例如:
繁忙假象 用define语句装饰编造出来的函数,给这些函数一堆参数注释,例如:
用延长技术隐藏变量 把
中xy_z替代成2行的
这样,全局搜索xy_z会什么也搜不到。对于C的预编译器来说,行末的"\"符号会把这一行和下一行合并到一起。 伪装成关键字的任意名字 写文档的时候,随意用一个file之外的词来重复文件名。不要用那些看起来就特别随意的名字,比如“Charlie.dat”或者“Frodo.txt”。一般情况下,用那些看起来更像保留的关键字的词。例如,给参数和变量起的比较好的名字有bank,blank, class, const, constant, input, key, keyword, kind, output, parameter,parm, system, type, value, var 和 variable。如果你用真正的保留关键字,编译器或者处理器会报错。这个技巧如果用得好,程序员会在真正的关键字和你起的名字之间感到绝望般的困惑。但是你还是可以看起来非常无辜,你声称这样命名是为了帮助他关联每个变量的用途。 代码命名不匹配跟屏幕显示的名字 选择跟显示在屏幕上的名字完全没有联系的变量名。例如,屏幕上显示标签名字是“Postal Code”,但是在代码里,变量起名为zip。 不要改变命名 用多个TYPEDEF给同一个符号起别名的方式替代全局同步重命名两个区块的代码。 如何隐藏被禁止的全局变量 由于全局变量被认为是‘罪恶的’,你可以定义一个结构体容纳所有的全局内容。机智地把这个结构体命名为类似EverythingYoullEverNeed(你所需要的每件事)的名字。让所有的函数都有一个指向这个结构体的指针(命名为handle可以迷惑更多)。这样会给别人你不是在使用全局变量的印象,你是通过一个handle还访问每件事的。然后定义一个静态实例,这样所有的代码就都是访问同一个副本了。 用同义词隐藏实例代码维护者修改代码之后,为了查看是否会有什么级联影响,会用变量名进行一次全局搜索。这可以通过同义词轻松化解,例如
这些def语句应该分散在不同的头文件中。这些头文件如果分散在不同目录效果更佳。另外一种技术是在每个块中重用变量名。编译器可以把他们分辨开来,但是简单的混合式文本搜索区分不了。不幸的在接下来几年到来的SCID(译注:Source Code in Database,一种将代码预解析然后存在数据库的技术)将会使得这项简单的技术不起作用,因为编辑器将会和编译器一样支持区块规则。 长长的相象的变量名每个长长的变量或者类名都不相同,只差一个字符或者只有大小写。一个完美的例子是swimmer和swimner。由于大多数字体的 发音类似拼写类似的变量名只有大小写或者下划线差别的变量可以很好的让那些用发音或者单词拼写来记忆变量而不是精确拼写的人感到凌乱。 重载和混乱在C++中,用#define重载库函数。这样看起来就像是你在使用一个很熟悉的库函数,但是实际上是完全不相同的东西。 选择最好的操作符重载例如,在C++中,把+,-,*,和/重载和加减完全不相关的操作。既然Stroustroup(C++作者)可以用移位操作进行I/O操作,为什么你不能那样充满创意呢?如果你重载+,一定要重载成i = i + 5 和i += 5的意义完全不同。来个一个例子将符号重载混乱上升到一个更高的艺术。为类重载!操作符,但是这个重载跟取反或者取否完全没关。让这个操作符返回一个整数。这样,为了或者一个布尔值,你必须用!!。而为了反转这个布尔值,[此处应有掌声]你就必须用!!!。不要混淆返回布尔0或者1的!操作符和按位取反的~逻辑操作符。 重载new重载 #defineC++自带的 #ifndef DONE #ifdef TWICE // put stuff here to declare 3rd time around void g(char* str); #define DONE #else // TWICE #ifdef ONCE // put stuff here to declare 2nd time around void g(void* str); #define TWICE #else // ONCE // put stuff here to declare 1st time around void g(std::string str); #define ONCE #endif // ONCE #endif // TWICE #endif // DONE 当传递 编译器指令编译器指令的设计目的是使同一代码的行为完全不同。 反复和有力的打开和关闭布尔短路指令,以及长字符串指令。 文档任何傻瓜都能说真话,但需要有些许理智的人,才懂得如何圆谎。 - 塞缪尔·巴特勒(1835 - 1902) 不正确的文档通常比没有文档更糟糕。 - Bertrand Meyer 由于计算机忽略注释和文档,你可以肆无忌惮,尽你所能的来迷惑那些可怜的维护代码的程序员。 在注释中说谎你不必主动说谎,只需使注释与最新的代码保持不一致就行了。 为那些显而易见的代码写文档为代码添加一些类似于/ * add 1 to i * /的注释,但是,绝不要为代码记录类似于包或方法的整体目的的东西。 记录怎么做而不是为什么这么做仅记录程序的细节,而不是它试图完成什么。 这样,如果有一个错误,改代码的程序员将不知道代码应该做什么。 避免记录“显而易见的”例如,如果您正在编写航空预订系统,如果您要添加另一家航空公司,请确保在代码中至少有25个地方需要修改。 不要记录他们在哪儿改动的。 那些在你之后接手代码的人,在没有彻底理解代码的每一行之前是无法开展修改代码的工作。 关于文档模板的正确使用考虑用于允许代码的自动化文档的函数文档原型。 这些原型应该从一个函数(或方法或类)复制到另一个,但不会填充字段。 如果由于某种原因被迫填写字段,请确保所有参数的名称对于所有函数都是相同的,并且所有注意事项都是相同的,但是当然不与当前函数相关。 关于正确使用设计文档当实现一个非常复杂的算法,使用经典的软件工程原理做一个声音设计编码之前。 编写一个非常详细的设计文档,描述一个非常复杂的算法中的每个步骤。 本文件越详细越好。 事实上,设计文档应该将算法分解成结构化步骤的层次结构,在文档中自动编号的单个段落的层次结构中描述。 使用至少5级深度的标题。 确保当你完成后,你完全破坏了结构,有超过500个这样的自动编号的段落。 例如,一个段落可能是(这是一个真实的例子) 1.2.4.6.3.13 - 显示所有可能的应用(短伪代码....), 然后... (这是在发牢骚) 当你写代码时,对每一个段落都写一个对应的全局函数,就叫 Act1_2_4_6_3_13() 不用给这些函数专门写文档了。 毕竟,设计文档里都有! 设计文档自动编号以来,就极难和代码保持同步更新了, (当然,因为函数名是静态的, 不是自动编号的) 。其实这根本不是问题,因为你从没想过文档同步更新这回事。事实上, 你还能做更多的事来摧毁设计文档可能有的任何用途。 那些在你后面来的人应该只能找到一两个矛盾的早期草稿的设计文件,这些文件隐藏在靠近死机的286电脑附近一些灰尘的货架上。 测量单位不要记录任何变量,输入,输出或参数的测量单位。 例如 英尺,米,纸箱。 这在bean计数中不是那么重要,但它在工程工作中非常重要。 作为推论,从不记录任何转换常数的度量单位,或如何导出值。 这是温和的作弊,但非常有效,在代码中混合一些不正确的计量单位的注释。 如果你感觉特别恶毒,补上自己的计量单位; 把它命名为你自己或一些模糊的人,永远不要定义它。 如果有人挑战你,告诉他们你这样做,以便可以使用整数而不是浮点运算。 陷阱永远不要为陷阱代码写文档。 如果你怀疑在一个类里可能有bug, 不要理睬它,自己知道就好。如果你对于这些代码该如何重组或重写的话, 听我的,不要把他们写出来。 记得在电影“小鹿斑比”有一句话: “如果你不能说什么好的, 就什么都不要说。” 吗? 万一那个写这段代码的人看到你的评论呢? 又或者如果公司的领导看到了呢? 或者是客户看到了呢?你可能会因此丢掉工作的。 一个人匿名留下的评论“这需要被修复”可能会产生疑问, 特别是在当这段评论所指的对象不明的情况下。 保持模糊,没有人会觉得自己被批评。 为变量写文档永远不要 为一个变量声明写注释。像关于这个变量如何使用,它的界限、合法值、隐式或者显式的十进制的值、度量单位、显示格式、数据输入规则(比如:都填入,必须输入),以及什么值是可以信任的,诸如此类信息,都应该可以通过程序代码获取到。 如果你的老板强制你写注释,只用给方法写注释就可以了,但是不要对一个变量声明做注释,即使是临时的也不要写! 在注释中贬损他人不建议任何企图使用外部维护承包商的行为,通过在你的代码里带有引用侮辱其他领先的软件公司的言论,特别是任何被承包过来工作的人。比如: /* 内循环优化 对在软件服务公司的笨蛋而言,这个员工太聪明了,他 通过利用<math.h>中垃圾一样的功能节约了50倍的时间和内存. */ class clever_SSInc { .. . } 如果可能的话,在代码的重要区块侮辱这个职员,调整注释的位置,这样管理者在急着把代码发给他自己的管理者的时候,还想把这块注释删掉的话,就会破坏代码原有的功能。 评论穿孔卡片CØBØL拒绝接受科技设备竞赛进展。尤其是 SCIDs。有谣言说所有的函数和变量声明一点就能出来,还说被Visual Studio 6.0开发出来的代码是被维护工程师用 edlin 或者 vi 维护的,别信它们。坚持严格的注释规则也会埋葬代码工作者乐趣。 蒙蒂的蟒蛇(指除了你在自己谁也不知道的代指)对一个叫makeSnafucated的函数写个 程序设计The cardinal rule of writing unmaintainable code is to specify each fact in as many places as possible and in as many ways as possible. 写不可维护代码的基本规则是把要素在尽可能多的地方用尽可能多的方式反复的说。 - Roedy Green 写可维护代码的关键是把程序的所有要素列在同一个地方。改变你的想法,应该把它们列在同一个地方,并保证程序仍然工作。因此,写不可维护性代码的关键是在尽可能多的地方,尽可能多的方式一遍又一遍的列举要素。欢呼吧,像Java那样的语言,有自己的独特方式,能让编写不可维护的代码变得更加简单。举个例子,它几乎不可能改变外部引用,因为所有的造型和数据类型转换都不工作,并且相关临时变量也不合用, 进一步, 如果变量显示在屏幕上, 所有相关联的变量和数据条目都要人工修饰并手动跟踪。Algol语言家族,包括C和Java,能用数组,哈希表,普通文件和不同类型的数据库存储数据。类似Abundance的语言,扩展了Smalltalk的,语法都很独特。仅仅是改变声明,利用Java的迟钝,把会慢慢溢出RAM的数据,放到数组里。这样维护工程师就有了一个把数组转成文件的可怕任务。同样的,在数据库中放小文件,这样维护工程师在性能调优时,就又有了把它们转成数组的乐趣。 Java 类型转换Java 的类型转换方法算是上帝赐予你的礼物。你可以毫无内疚的使用这个技术,因为语言本身需要它。每当你从集合中获取一个对象时你必须将之转换成其原始的类型。虽然变量的类型可以在很多地方指定。如果类型在后来发生了变化,那么所有转换的操作都必须与之匹配。而且编译器无法帮助那个倒霉的维护程序员检测到这些变化(因为实在太多了)。同样的,例如一个变量从 short 变成 int ,那么所有匹配的转换需要相应的改动。不过越来越多的开发人员使用泛型来解决这种问题。而且可以确定的是这个异端将不会进入语言的规范中。在 RFE 114691 中投票反对并在泛型上消除任意转换的需要(编者注:最后这两句话意思有点乱)。 利用 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++ 里尽量多使用友好声明。再把创建类的指针传递给已创建类。现在你不用浪费时间去考虑接口。另外,你应该用上关键字 private 和 protected 来表明你的类封装得很好。 使用三维数组大量使用三维数组,然后用不一样的方式在数组之间移动数据,比如,用 arrayA 的行去填充 arrayB 的列,再加上 1 的偏移值。这么做足以让程序员抓狂。 混合与匹配存取方法和公共变量同时用上。这样一来,你无需调用存取器的开销就可以修改一个对象的变量,还能宣称这个类是个”Java Bean”。程序员可能会试图添加日志函数来找出改变值的源头,但这一招会让他迷惑不已。 包装,包装,再包装无论什么时候,你使用别人写的代码,都要用至少一次的包装器把别人的脏代码与自己的隔离开。毕竟,其他作者 可能未来某个时间不顾一切地重命名了所有方法。到时候你怎么办?当然有办法。 如果他这样做,写个包装器就能将代码与更改隔离了,或者也可以让VAJ来处理全局性的重命名。然后,这是一个完美的借口,在别人做任何错事之前,先发制人的用一个中间层来切断和他的联系。Java的一个主要缺点是:如果不做愚蠢的方法包装,除了调用同名方法之外,就做不了任何事,即使只是想解决一个小问题。这意味着完全可能写一个四层深的包装却什么也不做,没人会注意到这一点的。为了最大化混淆效果,每一层都重命名方法,找个同义词典,挑点同义词。这会让人产生幻觉,以为你做了什么。 进一步,重命名有助于确保项目术语缺乏一致性。为了确保没有人试图把你的包装调整到一个合适的层次,在每一层都找点你自己的代码调用一下。 包装包装,更多包装在单独源文件的函数定义中,确保所有 API 函数至少包装了 6-8 次。 你也可以使用 #defines 的快捷方式来实现对这些函数的包装。 不保密!把每个方法和变量都声明为 public。毕竟总会有人有用得到它的时候。一旦方法被声明为 public 了,就很难回退。这样任何它覆盖到的代码都很难再修改。它还有个有趣的小功能,就是让你看不清类的作用是什么。如果老板质问你是不是疯了,你就可以告诉他,你遵循的是经典的透明接口原则。 爱经这种技术具有驱动程序包的任何用户或记录器以及维护程序员分心的附加优点。 创建十几个重载变量的同一方法,只有最细微的细节不同。 我认为是奥斯卡王尔德,观察到Kama Sutra的位置47和115是相同的,除了在115的地方女人的手指是交叉的。 包的用户必须仔细地阅读长列表的方法,以找出使用哪个变量。 该技术还使文档膨胀化,从而确保其更可能过时。 如果老板问你为什么这样做,解释它只是为了方便用户。 再次为了充分达到效果,克隆任何共同的逻辑,坐下来等待它的副本逐渐失去同步。 交换和掩饰交换参数,把这样的函数 主题和变体与其将参数用于单个方法,不如尽可能的创建多个单独的方法 。举个例子,有一个方法是 静态真好尽可能的多写静态变量。如果在程序里,你不需要某个类多于一个的实例,别人也不会需要的。重复,如果别人抱怨,告诉他们这样存取速度更快。 嘉吉猜想学习嘉吉猜想(我觉得嘉吉是男的) "任何设计问题都可以通过增加中间层次解决,即使是中间层太多的问题"。 分解面向对象的程序,直到搞不清究竟哪个对象负责改变程序状态为止。 更好的是,安排所有的回调事件激活都要通过指针森林,就是那个包含了程序可能用到的所有函数指针的森林。激活遍历森林的一个副作用就是,在释放对象引用计数指针之前就创造一个深拷贝,即使实际上不需要深拷贝。 全堆一块 把你所有的没用的和过时的方法和变量都留在代码里。毕竟说起来,如果你在 1976 年用过一次,谁知道你啥时候会需要再用到呢?虽然程序是改了,但它也可能会改回来嘛,你就说”不想重新发明轮子”(领导们都会喜欢这样的口气)。如果你还原封不动地留着这些方法和变量的注释,而且注释写得又高深莫测,甭管维护代码的是谁,恐怕都不敢对它轻举妄动 这就是 Final把所有的叶子类都声明为 final。毕竟说起来,你在项目里的活儿都干完了,显然不会有其他人会通过扩展你的类来改进你的代码。这种情况甚至可能有安全漏洞。 java.lang.String 被定义成 final 也许就是这个原因吧?如果项目组其他程序员有意见,告诉他们这样做能够提高运行速度。 不用接口鄙视Java的接口吧! 如果你的上级工程师抱怨,就告诉他们Java强迫你在两个不同的类(同样实现了这个接口)之间"剪切"代码,这样他们就知道有多难维护了。 而且,像Java AWT设计者一样工作,写一大堆功能,但只能被子类调用,再在方法里加上一大堆"instanceof"类型检查. 这样,如果有人想重用你的代码,他们必须继承你的类。如果他们想重用你的属于两个不同的类的代码 - 真不走运,他们不能同时继承两个类! 如果非得用接口,起一个万能的名字,比如 不用布局管理器永远不用布局管理器。 这样当维护程序员添加一个或更多组件时,他必须手动调整屏幕上每一个组件的绝对位置。 如果你老板强迫你使用布局管理器, 就用个大的 环境变量如果你必须为其他程序员编写类,在你的类里添加环境检查代码( 表格驱动逻辑避免任何形式的表驱动逻辑。 开始看上去再简单不过了, 但是最终使得用户会对校对不寒而栗,甚至是简单的修改表格。 改他妈的字段在Java里,所有私有数据类型是以传值的方式传参,所有传递的参数真的是只读的。 调用者可以随便修改参数,被调用者不会受到任何影响。 相对的,被传递过来的对象是可读写的。引用是按值传递的,但被引用的对象是同一个。 调用者可以对传过来的对象字段做任何想做的事。不用记录方法事实上有没有修改传递过来的对象。给你的方法起一个好名字,使它看上去仅仅是查看了对象的值,但实际上却进行了修改。 全局变量的魔力别使用异常来处理进程错误,把你的错误信息存到一个全局变量里。确保系统的每一个长循环都检查全局标签,并在发现异常时终止。再添加一个全局变量用来标识用户点击了'重置'按钮。当然了,系统里的所有主要循环也得检查第二个标签。 还有些不需要终止的循环,隐藏起来吧。 全局变量,再强调也不为过!如果上帝不想让我们使用全局变量,就不会发明它了。 别让上帝失望,尽可能多的设置和使用全局变量吧。每个函数都应该用上几个,即使实际上不需要也行。毕竟,任何好的维护工程师都会很快发现这是一次侦探锻炼,他也会很开心的意识到,这个锻炼能测试出一个人究竟是不是优秀的维护工程师,抑或只是个新来的而已。 全局变量,要多用,亲爱的全局变量完全把你从指定函数参数的工作中解救出来了。利用这个优势。 选择这些全局变量中的一个或多个指定其他进程要做什么。维护工程师居然愚蠢的认为C函数没有副作用。 确保他们想不到你在全局变量存储结果和内部状态信息。 副作用在C里, 函数应该是幂等的(没有副作用)(幂等:同一个函数在参数相同的情况下执行结果相同)。我希望这个提示足够明显。 回退在循环体里,假装循环必定成功,而且会立即更新所有的指针变量。如果随后在循环过程中出现了一个异常, 回退 被作为循环体条件并步进的指针。 局部变量(这一段正文和上一段正文完全一样,可能是文章上传的时候就错了?) 在循环体里,假装循环必定成功,而且会立即更新所有的指针变量。如果随后在循环过程中出现了一个异常, 回退 被作为循环体条件并步进的指针。 减少, 重用, 回收 ( Reduce, Reuse, Recycle,3R原则?)如果你需要定义一个结构体来保存数据回调,总把这个结构体命名为 配置文件它们通常是键值对类型的。那些值将在启动时被加载。最棒的混淆技术是给java变量及键们使用只有轻微不同的名字 把那些运行时也不改变的常量也放在配置文件里。参数文件变量至少需要比比一个简单变量至少多5倍的维护代码才行。 臃肿的类以最蠢的方法确保你的类有界,确保每个类都引用了外围的,模糊不清的方法和属性。举个例子,一个定义天体轨道几何的类当然应该有计算海洋潮汐时间表的方法,并包含起重机天气模型的属性。不仅在定义类的时候要这么干,而且当然也得在系统代码里找机会调用,比如需要在垃圾场里找吉他的时候。 该死的子类对于写不可维护的代码,面向对象编程简直是上天送来的礼物。如果你想创建一个类,有10个成员(属性/方法),那你应该考虑这种写法:写一个基类,只有一个成员,继承9层,每层添加一个成员。这样,到最后一层时,你就具有了全部的10个成员。如果可能,将每个类放到不同的文件中,这将更好的膨胀你的 代码混淆Sedulously eschew obfuscatory hyperverbosity and prolixity. (坚决避开混淆,冗长,和啰嗦) C的混淆参与网上的C混淆大赛并学习大师的一鳞半爪。 找到一个Forth或者APL大师在那个世界,代码越简洁,工作方式就越诡异,你就越受人尊重。 来一打能用两三个辅助变量解决的问题就别用一个变量解决 寻找模糊永不停止寻找解决常规问题的更模糊的方法。例如,别用数组将整数转换为相应的字符串,要使用类似这样的代码: 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 * 形式传递,然后再造型为合适的结构。不用结构而是通过位移字节数来造型也很好玩。 嵌套 SwitchSwitch 里边还有 Switch,这种嵌套方式是人类大脑难以破解的。 Exploit Implicit Conversion记住编程语言中所有微妙的隐式转换规则。 充分利用它们。不使用图片变量(picture variable) (in COBOL or PL/I) 也不用常规类型转换 (比如 原生整数使用组合框时,在switch条件中使用整数而不是定义好的常量 分号!只要语法允许,多用分号。 举个例子: if(a); else; { int d; d = c; } ; 用八进制在列好的十进制的数字里夹杂一点八进制数,像这样: array = new int [] { 111, 120, 013, 121, }; 类型转换在类型转换时java提供了很多混淆机会。一个简单的例子:如果你要将一个Double转换为字符串,曲折一点,使用 嵌套尽可能的嵌套。好的程序员能在一行写10组(),能在一个方法里写出20组 直接写数字如果你有一个有100个元素的数组, 尽可能多地在程序中直接写100。 别写静态常量,也别引用 这由来已久的技术在拥有两个不相干的数组恰好都有100个元素的程序中尤其有效。如果维护程序员擅自改变其中一个数组的长度,他必须解读程序中所有使用100进行迭代的for循环,以分辨出究竟哪个循环依赖了这个数组。几乎可以肯定,他至少会造成一个错误,希望多年后不会出现另一个。 还有更残忍的变体。能让维护程序员陷入一种虚假的安全感,看上去尽职尽责的命名了常量, 却经常偶尔地"意外"使用100这个值而不是定义好的常量。最最残忍的是, 偶尔使用一些其他无关的,仅仅恰好值是100的常量。现在, 毋庸置疑, 你应该避免任何一致的命名方案,也不在声明数组时用常量定义大小了。 C数组的新视角C编译器 int myfunc(int q, int p) { return p%q; } // ... myfunc(6291, 8)[Array]; 不幸的是,这些技术只能用于本地化的C类,java不让。 长行把尽可能多的代码打包成单行。这节省了临时变量的开销,还能通过消除换行符和空格使源文件变短。提示:移除操作符周围的所有空白。 好的程序员经常会困扰于一些编辑器强加的255字符行长度限制。好处是长行让那些不能阅读6号字的程序员必须拖动滚动条才能看完这一整行。 异常我要让你知道一个鲜为人知的秘密. 后台异常的出现是一个错误. 正确编写的代码永不失败,所以异常实际上是不必要的。不要在它们身上浪费时间。子类异常是无能的人知道他们的代码将失败才写的。你可以大大简化你的程序,在整个应用程序 (或main方法) 只写一个 try/catch然后调用System.exit()就行了。有一个完美的标准值得坚持,在每一个方法头声明Exception,不管实际上有没有任何异常要抛。 善用异常在不需要异常的语句里使用异常。一般使用 放弃多线程标题说明一切 (title says it all) 代码标准了解语言在新闻组里各种各样关于花式的位运算魔法代码的争论 e.g. *++b ? (*++b + *(b-1)) 0 就不是由语言规范定义的,每个编译器都可以自由地按不同的顺序进行解析. 这使它更加致命。同样, 好好利用C和Java移除所有空格后复杂的解析规则. 尽早return完全遵守no goto,no early returns的建议,也别写标签,这样和至少5层深的 if/else 嵌套再搭配不过了。 不用{}除非语法要求,否则不用 来自地狱的制表符永远不要低估你可以通过制表符来造成多少破坏,只需要用制表符代替空格,尤其是公司没规定一个制表符代表几个空格的时候。 让字里行间充满制表符,或者使用工具来完成空格对制表符的转换也很好。 魔法数组在某些数组位置使用特殊值作为标签。一个好的例子是 再探魔法数组如果需要给定类型的多个变量,定义一个数组就行了,然后用下标访问。只有你知道每个下标的意义并且不在文档里记录。也不要费心去用 不做美化不要使用自动的代码整洁(美化)工具保持你的代码规整. 在你的公司用人们在PVCS/CVS (版本控制跟踪工具) 创造了虚假的代码行数的理由说服他们放弃 ,并且每个程序员都应该有他自己的缩进风格,并在他写的任何永远神圣不可侵犯的模块中体现出来。 坚持其他程序员在他们自己的模块中也是这么干的。拒绝美化十分简单,即使要耗费数以百万计的击键来人工对齐,并消耗大量的时间来搞清楚那可怜的,个人风格的,对齐。 只要坚持每个人不只是在存储在公共库里时,还在他们编辑时, 都这么对齐 。这将让老板为了实现RWAR,禁止自动对齐。没有自动对齐,你可以自由的 意外 错位代码,形成视觉错觉,让看上去正常的方法体比实际上更短或者更长,或者代码运行时才发现else分支匹配了一个出乎意料的if。 e.g. if(a) if(b) x=y; else x=z; 宏预处理器它提供了巨大的混淆机会,这项技术的关键点是几层深的嵌套宏展开,你应该把宏命令分散在各个不同文件中,将可执行代码换成宏然后分散到各个 *.cpp 文件中 (即使是那些从来不用的宏),这将使代码变更的编译需求数量最大化 。 混合数组声明Java 有两种数组声明方式。老版的C也能这么做,老式的 byte[ ] rowvector, colvector , matrix[ ]; 类似于: byte[ ] rowvector; byte[ ] colvector; byte[ ][] matrix; 隐藏错误恢复代码使用嵌套将函数的错误恢复方法尽可能远离调用的位置。一个简单的例子就是设置了 10 或者 12 级的嵌套: if ( function_A() == OK ) { if ( function_B() == OK ) { /* Normal completion stuff */ } else { /* some error recovery for Function_B */ } } else { /* some error recovery for Function_A */ } 伪 C
混乱的导入让维护程序员去猜测你使用的方法和类究竟来自那一个包。 不要这样写: import MyPackage.Read; import MyPackage.Write; 应该用: import Mypackage. *; 无论多么晦涩也不完全限定任何方法或类。 让维护程序员尽情猜测你使用的包/类的来源。当然啦,对你帮助最大的导入方式肯定不是全限定导入。 厕所油管(又臭又长?)在任何情况下,决不能允许在屏幕上出现多于一个函数或过程的代码。实现这一点很简单,使用这个方便的技巧就行: 空行通常用于分离代码的逻辑块。每行本身都一个逻辑块. 在每行之间放上空行。 不要在代码的结尾写注释。 把它放在这一行的上面. 如果你被迫在行尾写注释,在整个文件中选择最长的代码行,加上10个空格,然后将所有其他行注释的开头与该列对齐。 方法顶部的注释需要使用模板,而且最少包含 15 行,包括很多的空行。下面是具体的例子: /* /* Procedure Name: /* /* Original procedure name: /* /* Author: /* /* Date of creation: /* /* Dates of modification: /* /* Modification authors: /* /* Original file name: /* /* Purpose: /* /* Intent: /* /* Designation: /* /* Classes used: /* /* Constants: /* /* Local variables: /* /* Parameters: /* /* Date of creation: /* /* Purpose: */ 在文档中放置这么多冗余信息的这项技术,可以让它很快的过时,并有助于负责维护的程序员傻到去相信它。 测试I don't need to test my programs. I have an error-correcting modem. (我不需要测试我的程序。我有一个错误纠正调制解调器。)
把Bug留在程序里,留给新来的维护工程师,留给寻找有趣事情的后来者。一个完美的Bug从不留下任何可能解释本身出现位置或原因的线索, 最懒,但也最有效的方法就是从来不测试你的代码. 拒绝测试从不测试任何代码,包括错误处理,机器崩溃或系统故障。也不检查操作系统的返回代码。这种代码从来不会被执行,只会减慢测试时间而已。 而且, 你怎么可能测试到代码对硬盘错误,文件读取错误,系统崩溃或其他任何类型问题的处理方式呢。为什么?你怎么可能有一个令人难以置信的不可靠的计算机或能模仿这样事情的测试脚手架呢? 现代硬件永不失败, 谁想只是为了测试目编写代码呢?它没有任何乐趣。如果用户抱怨,,怪操作系统或硬件就行了。 他们不会知道真相的。 永不,绝不做任何性能测试伙计,如果程序不够快,告诉客户买台更快的机器就好了。如果你做了性能测试,就可能发现性能瓶颈,就可能需要改变算法啊, 就可能需要重构整个产品. 谁想那么做? 而且, 在客户现场上出现的性能问题意味着一次免费体验异国情调的旅行,只要换个新镜头并准备好护照就行了。 别写测试用例拒绝执行代码覆盖或路径覆盖测试,自动化测试给窝囊废用的。 自己找出哪些功能占你日常使用的90%,并分配90%测试到这些路径。毕竟,这个技术可能只测试你源代码的60%,直接节省了40%的测试工作量.这可以帮助你在项目后期补偿计划量表。很久以后才会有人注意到那些美妙的“营销特性”不工作。 著名的大型软件公司就是这样测试代码的; 你也应该这样. 如果看到这里你还没走,请参阅下一项. 懦夫才测试勇敢的程序员从不测试。太多的程序员害怕老板,害怕失去工作,害怕客户的指责邮件,害怕被起诉了。这种恐惧麻痹了热情,降低了工作效率. 研究表明,消除测试阶段意味着管理人员可以提前确定上线日期,这对工作计划有着明显帮助。恐惧消失,创新和实验将开花结果。程序员的角色是生成代码,debug是帮助台和维护工程师的事,互相合作嘛。如果我们对我们的编码能力有充分的信心,那么测试就没有必要。从逻辑上看,傻瓜都知道测试解决不了任何技术问题,相对的,这只是一个情感信心的问题。对缺乏信心这个问题,一个更有效的解决方案是完全消除测试并送我们的程序员去上建立自信的课程. 毕竟,如果我们选择做测试,我们就得测试程序的每个变化, 但现在我们只需要送我们的程序员去上一节课来建立自信就成了,这可是惊人的成本效益啊。 确保程序只在调试模式下工作如果你定义 TESTING 为 1 #define TESTING 1 这给了你一个很好的机会,插入单独的代码段,如 #if TESTING==1 #endif 它可以包含一些不可或缺的代码,比如 x = rt_val; 所以,如果有人把TESTING改成了0,这程序就将停止工作。只要再加上一小点想象力,不仅能做到逻辑混乱,还能让编译器也混乱。 语言的选择Philosophy is a battle against the bewitchment of our intelligence by means of language.
电脑语言正在变得越来越愚蠢,使用那些新的静态语言简直反人类。 坚持使用你能侥幸找到的最老的语言,如果可以的话,用八进制的语言(像Hans und Frans一样,我不是娘娘腔的男人;我很有男子气概的,我用漆金线连接IBM单元记录设备 (穿孔卡片), 并用手在纸带上打孔实现编码), 汇编不够格,FORTRAN和COBOL也不够格, C和BASIC不不够格, C++也不够格.,全都不够格。 FORTRAN用 FORTRAN写代码。如果你的老板质疑你,你就告诉他FORTRAN上有很多有用的库可以节约时间,即使用 FORTRAN写的代码的可维护性是0,这种编写不可维护代码的规则是很容易遵循的. 拒绝 Ada这些技术大约有20%不能用于 Ada,拒绝它吧。如果老板强迫你用,坚持回答别人也不用,并且指出它会让你的大量工具失效,比如 lint 和 plummer,这可都是能有效利用C缺陷的法宝啊。 使用 ASM把所有公共的效用函数转换成asm. 使用 QBASIC确保所有重要的库函数用QBASIC编写 , 然后简单的写一个asm包装器就能处理大- >中内存模型映射。 写点汇编在程序里随机写点汇编多有意思啊,几乎没有人懂,即使是几行也能冷却程序员的维护妄想。 汇编与C如果你有被C调用的汇编模块,尽可能多的使用它,即使是最微不足道的目的,也要确保充分利用了goto,bcc或者其他迷人的汇编习俗。 不用维护工具不适用Abundance编码, 也别用任何其他语言的编码工具,它们从一开始就被设计的主要目就是使维护工程师的工作变简单 。 同样,避免使用 Eiffel 或者 Ada,它们从一开始就是为了 在项目进入生产之前捕捉bug 而设计的 。 与人交流__Hell is other people._ _(地狱就是别人)
这里有很多灵光一现的点子,能让那些喋喋不休的维护工程师变的沮丧,能击溃你的老板阻止你写不可维护代码的妄想,甚至煽动起一场关于每个人在库中的代码格式应该是什么样子的争论. 老板全都懂如果你的老板认为他/她自己20年的FORTRAN编程经验足以指导现代编程,严格遵守他/她的所有建议。这样,老板会信任你。这对你的职业生涯有益,而且你会学到很多新的方法来混淆程序代码。 颠覆帮助台确保代码充满bug的方法之一是确保维护人员从不了解帮助台. 这就需要颠覆帮助台. 不接电话. 使用自动语音,回复 "感谢你来售后服务热线。 需要人工服务请按“1”或者留下语音留言",忽略请求帮助的邮件,而不是给他们一个可用的电话号码。 对任何问题的标准回复是 " 我认为你的帐户被锁定了,但是有权解锁的人现在不在" 少说话不用在意下一个“千年虫”。如果你发现了什么东西在悄悄的向着摧毁西半球的所有生命前进,别公开讨论它,到我们处在最后四年,恐慌和机遇的关键事件窗口期再说。别把你的发现告诉朋友,同事或者任何其他有能力的人。 在任何情况下也不尝试宣扬这个信息也许暗示着新的或巨大盈利或威胁 。仅仅发送一个普通优先级,术语加密的备忘录给你的上级来履行职责(to cover-your-a$$) . 如果可能的话,就把这段术语加密的信息作为一个不相干又带着更紧迫的业务关注的的纯文本备忘录的附件。 放心,我们都看到了威胁。 安心的睡吧,很久之后,他们将用对数增长的时薪求提前退休的你回来 ! 瞎写的微妙微妙是一件有趣的事, 有时锤子比别的工具更容易微妙。试试微妙的注释,创造一个类,有着类似 在月俱乐部写书加入写电脑书的俱乐部,选择那些总是忙于写书以至于没空写代码的作者,浏览本地书店寻找那些有很多图却没有代码实例的文章,浏览这些书,学习那些空洞又学术的语言, 可以拿来恐吓那些追在你屁股后面的傻瓜们。你的代码应该富有特色。如果人们理解不了你的词汇,他们就会认为你极有才华而算法深奥。千万避免给你的算法任何易于理解的例子。 重造轮子你总是想写系统级别的代码,现在,机会来了。忽略标准库,写你自己的。放在简历上也好看。 自己的BNF总是使用你自己的,独一无二的,BNF无关的命令语法。不要试图在注释里为合法和非法命令提供配套的解释 .那被证明了完全缺乏学术严谨性,铁路运行时刻表从来都不准确。确保没有对从中间过渡符号(真的有合适的意思)到最终的符号(你最终键入的)演变做明显的解释。 永远也不使用字体,颜色,加粗或其他任何视觉线索来帮助阅读者区分这两者。在你自己的BNF规范里使用与程序完全相同的标点符号,这样阅读者就搞不清在你的BNF规范里 自己的内存分配每个人都知道调试动态存储复杂而费时。改造你的存储分配器而不是确保每一个类都没有内存泄露。强迫你的用户定期重启系统来清理堆而不是释放内存。 系统重启是一件很简单的事 -- 比修复所有的内存泄露漏洞简单多了 。只要用户记得定期重启系统,就不会发生运行时堆空间溢出。想象一下,这能改变他们 “一次部署” 的策略! 另类语言的技巧让你脑残的基本编程方法
SQL 别名给数据库表取一到二个字符长度的名称。最好是跟它毫无关系的已有表的表名类似。 SQL 外联 Outer Join混合各种外部连接语法,让大家看着头皮发麻。 JavaScript Scope"优化" JavaScript 代码要充分利用一个函数可以在调用者的范围内访问所有本地变量的特性。 Visual Basic 变量定义将如下定义: dim Count_num as string dim Color_var as string dim counter as integer 改为: Dim Count_num$, Color_var$, counter% Visual Basic 疯狂如果从一个文本文件读取 15 个字符,那么可以使用如下方法来让事情变得复杂: ReadChars = .ReadChars (29,0) ReadChar = trim(left(mid(ReadChar,len(ReadChar)-15,len(ReadChar)-5),7)) If ReadChars = "alongsentancewithoutanyspaces" Mid,14,24 = "withoutanys" and left,5 = "without" Delphi/Pascal不准使用函数和过程。使用 label/goto 语句,然后在代码中乱跳。这足以让他人在跟踪代码时发疯。另外就是让代码在不经意间来回跳跃,没错,就是这个窍门。 Perl在一行非常非常长的代码结尾使用 if 和 unless 语句。 LispLISP 一个非常梦幻般的语言,用来写不可维护代码。看看下面的这些令人困惑代码片段: (lambda (*<8-]= *<8-[= ) (or *<8-]= *<8-[= )) (defun :-] (<) (= < 2)) (defun !(!)(if(and(funcall(lambda(!)(if(and '(< 0)(< ! 2))1 nil))(1+ !)) (not(null '(lambda(!)(if(< 1 !)t nil)))))1(* !(!(1- !))))) Visual Foxpro如果一个变量未定义就不用使用,除非你给它赋值。当你检查变量类型时候就会发生这种问题: lcx = TYPE('somevariable') lcx 的值将是 LOCAL lcx lcx = TYPE('somevariable') lcx 的值现在是 LOCAL lc_one, lc_two, lc_three... , lc_n IF lc_one DO some_incredibly_complex_operation_that_will_neverbe_executed WITH make_sure_to_pass_parameters ENDIF IF lc_two DO some_incredibly_complex_operation_that_will_neverbe_executed WITH make_sure_to_pass_parameters ENDIF PROCEDURE some_incredibly_complex_oper.... * put tons of code here that will never be executed * why not cut and paste your main procedure! ENDIF 杂七杂八的技术如果你给某人一段程序,你会让他困惑一天;如果你教他们如何编程,你会让他困惑一辈子。
不要重编译让我们从一条可能是有史以来最友好的技巧开始:把代码编译成可执行文件。如果它能用,就在源代码里做一两个微小的改动 — 每个模块都照此办理。但是不要费劲巴拉地再编译一次了。 你可以留着等以后有空而且需要调试的时候再说。多年以后,等可怜的维护代码的程序员更改了代码之后发现出错了,他会有一种错觉,觉得这些肯定是他自己最近修改的。这样你就能让他毫无头绪地忙碌很长时间。 完败调试工具对于试图用行调试工具追踪来看懂你的代码的人,简单的一招就能让他狼狈不堪,那就是把每一行代码都写得很长。特别要把 then 语句 和 if 语句放在同一行里。他们无法设置断点。他们也无法分清在看的分支是哪个 if 里的。 公制和美制在工程方面有两种编码方式。一种是把所有输入都转换为公制(米制)计量单位,然后在输出的时候自己换算回各种民用计量单位。另一种是从头到尾都保持各种计量单位混合在一起。总是选择第二种方式,这就是美国之道! CANICANI 是 Constant And Never-ending Improvement 的缩写,持续改进的意思。 要常常对你的代码做出“改进”,并强迫用户经常升级 — 毕竟没人愿意用一个过时的版本嘛。即便他们觉得他们对现有的程序满意了,想想看,如果他们看到你又“完善“了它,他们会多么开心啊!不要告诉任何人版本之间的差别,除非你被逼无奈 — 毕竟,为什么要告诉他们本来永远也不会注意到的一些bug呢?
|
|