如何编写不可维护的代码;

永远不要把无能归咎于作恶------拿破仑

你不需要做的太过火了。你的代码不应该看起来毫无维护的可能性,就让它们那样。否则,它们可能面临着被重写或者重构的风险。

总则

他从一个卫生纸卷筒中窥视你的代码。他一次只能看到一小片你的代码。你应该确保他这样做是不可能看到代码的宏图框架的。你应该让他难以找到他想要找的代码。更重要的是,你应该尽力让他难以安全地忽视任何细节。

程序员总是被约定麻痹。偶尔违反下约定,你就能强迫得他们用一个放大镜一行一行读你的代码。

可能你已经得到让所有编程语言变得不可维护的诀窍了。----除此之外,就是适当的滥用一下这技巧。

命名

"WhenIuseaword,"HumptyDumptysaid,inaratherscornfultone,"itmeansjustwhatIchooseittomean-neithermorenorless."LewisCarroll--ThroughtheLookingGlass,Chapter6编写不可维护代码的一大技巧就是命名变量和函数的艺术。命名对编译器没有意义。但是给了你迷惑代码维护程序员的巨大空间。

买一本给小孩起名字的参考书,这样你在给变量命名时绝对不会吃亏。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(字母大写)

随机大写单词中的音节字母。比如,ComputeRasterHistoGram().

重用名字

只要语法规则允许,就给类,构造函数,函数,变量,参数和局部变量起相同的名字。另外,给{}中的局部变量起相同的名字。目的就是让维护者不得不仔细地检查每个实例的作用域。特别对于Java,把普通函数伪装成构造函数。

使用重音字母

在变量名里使用重音符号,比如

typedefstruct{inti;}ínt;这里第二个int的i实际上是重音的i。在简单的文本编辑器里,几乎不可能分辨出这个倾斜的重音符号。

利用编译器能识别的变量名长度限制

例如假设编译器只识别前8个字符,那么var_unit_update和var_unit_setup都会被认为和var_unit一样。

下划线,其实是一个朋友

用'_'和'__'作标识符。

随机点缀使用两种语言(人类语言和计算机语言)。如果你的老板坚持你使用他的语言,你就告诉他使用你自己的语言你能更好地组织你的想法。或者如果那样说不好使,就说这是语言歧视,然后威胁说要控告他们索赔一大笔钱。

扩展ASCII字符

扩展ASCII字符完全是合法的变量字符,包括,D,和字符。这些字符除了复制粘贴几乎不可能在一个简单的编辑器里编辑。

使用其他语言的名字

用数学运算符号做名字

使用伪装成数学运算符的变量名,例如:

openParen=(slash+asterix)/equals;(译注:单词意思是‘左圆括号’=(‘斜线’+‘星号’)/‘等号’)

使用使人眼花缭乱的名字

marypoppins=(superman+starship)/god;(译注:欢乐满人间(电影名)=(超人+星际飞船)/上帝)

每个人对词汇的情感内涵都有不同的的理解,所以这样会让读代码的人很困惑。

重命名和重复使用

这个技巧在Ada语言中工作的尤其好。Ada是一种免除了现代模糊技术的语言。命名你现在使用的对象和包的那些人都是白痴。与其通知他们修改,不如按照自己的发明进行重命名和子类型重命名所有的名字。当然保留一些原来的名字,作为一个粗心的陷阱。

何时使用i

千万不要把i用作最里层的循环变量。公平地把i用在任何其他地方,尤其是非整型变量的地方。类似的,用n做循环变量。

约定

小写的l很像数字1

用小写的l来标识long类型常数。例如10l更容易被误解成101。不使用那些能清楚分辨uvw,wW,gq9,2z,5s,il17|!j,oO08,`'",;,.,mnnrn,and{[()]}的字体。发挥你创造性。

重用全局名字做为私有变量名

在模块A定义一个全局数组,在模块B的头文件定义一个同样名字的私有数组变量。这样似乎你在模块B用的是全局数组,但其实不是。不要在注释里提及这里的复用。

循环访问

用一种矛盾的方式尽可能迷惑地循环使用变量名作用域。例如,假设有全局变量A和B,和函数foo和bar。A一般传递给foo,B传递给bar。那么把foo函数定义成foo(B)和bar函数定义成bar(A)。这样在函数内部,A会被当成B,B别当成A。等有更多的函数和全局变量,你就可以生成一张互相矛盾地使用相同名字的变量组成的大网。

回收利用变量

在变量和函数名里使用缩写时,对同一个单词用不同变体打破无聊,甚至偶尔速记一把。这帮助了打败那些使用文本搜索去理解你代码某个方面的懒汉。例如,混合国际方式拼写的colour和美国方式拼写color以及花花公子口中的kulerz。如果你全拼这些名字,只有一种拼写方式,这样太容易让代码维护者记住了。因为有很多不同的简写同一个单词的方式,使用简写你可以有很多不同的变量表示同一个表面上的意思。作为一个附加的福利,代码维护者可能甚至都没注意到他们是不同的变量。

误导人的命名

确保每个函数都比它的名字表达出的意思多做或者少做一点。举个简单的例子,一个名为isValid(x)的函数有个把x转成二进制且存进数据库的副作用。

m_

一个来自C++的命名约定是在变量名前使用m_前缀。这个约定只要你“遗忘性地”在函数也加个m,就可以告诉他们这不是函数。

o_appleobj_apple

在每个类实例前加o或者obj前缀,以表明你想表示一个巨大的,多形态的宏图。

匈牙利命名法是代码混淆技术的战术核武器,使用它!让大量的源代码被这种习语污染,没有什么能比这种精心策划的匈牙利命名法攻击更快的干掉一名维护工程师了!以下的提示将帮助你从本质上腐蚀匈牙利命名法:

在C++或其他允许使用常量性变量的语言中坚持使用"c"作为常量

寻找并使用那些只在你使用的语言里没有特殊意义的有(keng)趣(die)字符(匈牙利毒瘤/Hungarianwarts)。比如说坚持在PowerBuilder上使用l_anda_{localandargument}做变量名前缀,比如说坚持在写给C++的控件类型上显现出一种有趣的VB风格,坚持忽略数以兆计的MFC开放代码从来不使用那些有(keng)趣(die)字符做控件类型的事实。

最违反匈牙利原则的是最常用的变量总是随身携带最少的信息.很好的贯彻上述技术的方法是坚持为每一个类加上一个自定义的前缀。也不要让任何人告诉你,一般没有前缀的才是类!这条规则的重要性,毫不夸张的说,如果你不能坚持的话,源代码很快就会被具有较高元音/辅音比的短变量名淹没。最坏的情况,将导致代码充满自重复英语符号和各种模糊的崩溃!

悍然违反匈牙利风格的设计理念要求函数参数和其他常见符号必须被赋予一个有意义的名字,但是真正的匈牙利毒瘤风格完全由自己临时创造一个完美的变量名字。

坚持给你自己的有(keng)趣(die)字符携带一些完全无关的信息,考虑这个真实的例子,a_crszkvc30LastNameCol。它让一整组维护工程师花了三天才发现这个大家伙是(aconst,reference,functionargumentthatwasholdinginformationfromadatabasecolumnoftypeVarchar[30]named"LastName"whichwaspartofthetable'sprimarykey)的缩写!当适当的结合“所有的变量应该是公共的”这一原则,这种技术有能力使数千行的代码立即作废!

为了完美利用人脑只能同时容纳七条信息的原则,你可以写出具有以下特性的代码:

再说匈牙利命名法

一个匈牙利命名法中后续的招数是“改变变量的类型但是不改变变量名”。这几乎是从16位Windows时代一层不变地迁移来的:

WndProc(HWNDhW,WORDwMsg,WORDwParam,LONGlParam)到win32

WndProc(HWNDhW,UINTwMsg,WPARAMwParam,LPARAMlParam)这里的w都表明他们是单词,但实际上是指long类型。这种命名法迁移到win64时,意义会很明确,因为在win64就是参数的位宽就是64位。但是以前的w和l前缀,就呵呵了。

减少,重用,再利用

如果你为回调函数定义一个结构体来存储数据,把结构体命名为PRIVDATA。每个模块都可以定义它自己的PRIVDATA。在vc++里,这样做有个好处就是迷惑代码调试者。因为有很多PRIVATA展示在watch窗口,他不知道你指的是哪个PRIVATA,所以他只能任选一个。

晦涩地参考电影情节

用LancelotsFavouriteColour(译注:Lancelot最喜欢的颜色)替代blue给常量命名,并且赋值十六进制的$0204FB。这种颜色在屏幕上看起来和纯蓝色完全一样,但是这样命名会让代码维护者不得不执行程序(或者使用某些图像工具)才能知道是什么样。只有那些熟悉“巨蟒剧团”和《圣杯的故事》才知道Lancelot最喜欢的颜色是蓝色。如果代码维护者想不起来所有巨蟒剧团的电影,那他或者她就没资格做一个程序员。

Thelongerittakesforabugtosurface,theharderitistofind.-RoedyGreen编写不可维护代码一大部分技巧都是伪装的艺术。很多技巧都基于代码编译器能够比人眼和文本编辑器更好地分辨代码这一事实上。这里列举一些最佳伪装技术。

代码和注释相互伪装

来一些第一眼看上去是注释的代码。

for(j=0;j

如果没有编辑器颜色区分代码,你能注意到那三行已经别注释掉了吗?

struct/union和typedefstruct/union在C(不是C++)中是不同的命名空间。在这两个名字空间里给结构体和枚举使用相同的名字。如果可能的话,让他们几乎一模一样。

typedefstruct{char*pTr;size_tlEn;}snafu;structsnafu{unsignedcNtchar*pTr;size_tlEn;}A;隐藏宏定义把宏定义隐藏在垃圾注释中间。程序员就会因为无聊放弃读这些注释,于是就发现不了这些宏。确保这些宏替换看起来像一个完全合法的赋值操作,例如:

#definea=ba=0-b繁忙假象

用define语句装饰编造出来的函数,给这些函数一堆参数注释,例如:

#definefastcopy(x,y,z)/*xyz*///...fastcopy(array1,array2,size);/*doesnothing*/用延长技术隐藏变量

#definelocal_varxy_z中xy_z替代成2行的

#definelocal_varxy_z//local_varOK这样,全局搜索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。如果你用真正的保留关键字,编译器或者处理器会报错。这个技巧如果用得好,程序员会在真正的关键字和你起的名字之间感到绝望般的困惑。但是你还是可以看起来非常无辜,你声称这样命名是为了帮助他关联每个变量的用途。

代码命名不匹配跟屏幕显示的名字

选择跟显示在屏幕上的名字完全没有联系的变量名。例如,屏幕上显示标签名字是“PostalCode”,但是在代码里,变量起名为zip。

不要改变命名

用多个TYPEDEF给同一个符号起别名的方式替代全局同步重命名两个区块的代码。

如何隐藏被禁止的全局变量

由于全局变量被认为是‘罪恶的’,你可以定义一个结构体容纳所有的全局内容。机智地把这个结构体命名为类似EverythingYoullEverNeed(你所需要的每件事)的名字。让所有的函数都有一个指向这个结构体的指针(命名为handle可以迷惑更多)。这样会给别人你不是在使用全局变量的印象,你是通过一个handle还访问每件事的。然后定义一个静态实例,这样所有的代码就都是访问同一个副本了。

代码维护者修改代码之后,为了查看是否会有什么级联影响,会用变量名进行一次全局搜索。这可以通过同义词轻松化解,例如

#definexxxglobal_var//infilestd.h#definexy_zxxx//infile..\other\substd.h#definelocal_varxy_z//infile..\codestd\inst.h这些def语句应该分散在不同的头文件中。这些头文件如果分散在不同目录效果更佳。另外一种技术是在每个块中重用变量名。编译器可以把他们分辨开来,但是简单的混合式文本搜索区分不了。不幸的在接下来几年到来的SCID(译注:SourceCodeinDatabase,一种将代码预解析然后存在数据库的技术)将会使得这项简单的技术不起作用,因为编辑器将会和编译器一样支持区块规则。

每个长长的变量或者类名都不相同,只差一个字符或者只有大小写。一个完美的例子是swimmer和swimner。由于大多数字体的ilI1|或者oO08差别很小,利用这点,使用标识符例如parselnt和parseInt或者D0Calc和DOCalc做变量名是一个绝好的主意。因为随便一眼看去,都像是常量1.在很多字体里,rn看起来很像一个m。所以你知道变量swirnrner可以怎么做了。也可以制造一些名字仅仅差在大小写的变量名,例如HashTable和Hashtable。

只有大小写或者下划线差别的变量可以很好的让那些用发音或者单词拼写来记忆变量而不是精确拼写的人感到凌乱。

在C++中,用#define重载库函数。这样看起来就像是你在使用一个很熟悉的库函数,但是实际上是完全不相同的东西。

重载new操作符-比重载+-/*危险多了。这可能会造成巨大浩劫如果重载后的东西做了一些与它本来的功能完全不同的事情,(但是重要的对象功能很难改变)。确保用户创建动态实例难于登天。你还能表演一下这个有趣的戏法,不仅有个成员功能,还有个成员变量,都叫"New"。

#ifndefDONE#ifdefTWICE//putstuffheretodeclare3rdtimearoundvoidg(char*str);#defineDONE#else//TWICE#ifdefONCE//putstuffheretodeclare2ndtimearoundvoidg(void*str);#defineTWICE#else//ONCE//putstuffheretodeclare1sttimearoundvoidg(std::stringstr);#defineONCE#endif//ONCE#endif//TWICE#endif//DONE当传递g()一个char*时会非常有趣,因为调用不同版本的g()将取决于包含头的次数。

编译器指令的设计目的是使同一代码的行为完全不同。反复和有力的打开和关闭布尔短路指令,以及长字符串指令。

任何傻瓜都能说真话,但需要有些许理智的人,才懂得如何圆谎。-塞缪尔·巴特勒(1835-1902)

不正确的文档通常比没有文档更糟糕。-BertrandMeyer

由于计算机忽略注释和文档,你可以肆无忌惮,尽你所能的来迷惑那些可怜的维护代码的程序员。

你不必主动说谎,只需使注释与最新的代码保持不一致就行了。

为代码添加一些类似于/*add1toi*/的注释,但是,绝不要为代码记录类似于包或方法的整体目的的东西。

仅记录程序的细节,而不是它试图完成什么。这样,如果有一个错误,改代码的程序员将不知道代码应该做什么。

例如,如果您正在编写航空预订系统,如果您要添加另一家航空公司,请确保在代码中至少有25个地方需要修改。不要记录他们在哪儿改动的。那些在你之后接手代码的人,在没有彻底理解代码的每一行之前是无法开展修改代码的工作。

当实现一个非常复杂的算法,使用经典的软件工程原理做一个声音设计编码之前。编写一个非常详细的设计文档,描述一个非常复杂的算法中的每个步骤。本文件越详细越好。事实上,设计文档应该将算法分解成结构化步骤的层次结构,在文档中自动编号的单个段落的层次结构中描述。使用至少5级深度的标题。确保当你完成后,你完全破坏了结构,有超过500个这样的自动编号的段落。例如,一个段落可能是(这是一个真实的例子)

1.2.4.6.3.13-显示所有可能的应用(短伪代码....),

然后...(这是在发牢骚)当你写代码时,对每一个段落都写一个对应的全局函数,就叫

Act1_2_4_6_3_13()不用给这些函数专门写文档了。毕竟,设计文档里都有!

设计文档自动编号以来,就极难和代码保持同步更新了,(当然,因为函数名是静态的,不是自动编号的)。其实这根本不是问题,因为你从没想过文档同步更新这回事。事实上,你还能做更多的事来摧毁设计文档可能有的任何用途。

那些在你后面来的人应该只能找到一两个矛盾的早期草稿的设计文件,这些文件隐藏在靠近死机的286电脑附近一些灰尘的货架上。

不要记录任何变量,输入,输出或参数的测量单位。例如英尺,米,纸箱。这在bean计数中不是那么重要,但它在工程工作中非常重要。作为推论,从不记录任何转换常数的度量单位,或如何导出值。这是温和的作弊,但非常有效,在代码中混合一些不正确的计量单位的注释。如果你感觉特别恶毒,补上自己的计量单位;把它命名为你自己或一些模糊的人,永远不要定义它。如果有人挑战你,告诉他们你这样做,以便可以使用整数而不是浮点运算。

不建议任何企图使用外部维护承包商的行为,通过在你的代码里带有引用侮辱其他领先的软件公司的言论,特别是任何被承包过来工作的人。比如:

对一个叫makeSnafucated的函数写个/*makesnafucated*/就行了。别告诉别人snafucated是anywhere的意思.这还用说嘛,笨蛋才不知道snafucated的意思呢。Sun的AWT技术文档,就是这种技术的经典例子。

Thecardinalruleofwritingunmaintainablecodeistospecifyeachfactinasmanyplacesaspossibleandinasmanywaysaspossible.

写不可维护代码的基本规则是把要素在尽可能多的地方用尽可能多的方式反复的说。

-RoedyGreen

Java的类型转换方法算是上帝赐予你的礼物。你可以毫无内疚的使用这个技术,因为语言本身需要它。每当你从集合中获取一个对象时你必须将之转换成其原始的类型。虽然变量的类型可以在很多地方指定。如果类型在后来发生了变化,那么所有转换的操作都必须与之匹配。而且编译器无法帮助那个倒霉的维护程序员检测到这些变化(因为实在太多了)。同样的,例如一个变量从short变成int,那么所有匹配的转换需要相应的改动。不过越来越多的开发人员使用泛型来解决这种问题。而且可以确定的是这个异端将不会进入语言的规范中。在RFE114691中投票反对并在泛型上消除任意转换的需要(编者注:最后这两句话意思有点乱)。

Java要求你必须为每个变量指定两次的类型。Java程序员已经习惯了这种多余的操作,他们一般不会注意到这两种类型略有不同。例如:

Bubblegumb=newBubblegom();不幸的是++操作的受欢迎程度使得你很难逃避如下代码的伪冗余代码:

swimmer=swimner+1;从不做验证从不对任何输入的数据进行验证,不管数据是否正确或者是否有差异。这表明你绝对相信公司的设备,以及你正在一个完美的团队中工作。所有合作伙伴和系统运营商都是可信任的,他们提供的数据都是合理的。

有礼貌,无断言

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

避免封装

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

复制粘贴修改

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

使用静态数组

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

傻瓜接口

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

巨型监听器

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

好事成堆TM

大胆使用封装和OO思想。例如

myPanel.add(getMyButton());privateJButtongetMyButton(){returnmyButton;}这段很可能看起来并不那么有趣。别担心,只是还没到嗨点。

好朋友

大量使用三维数组,然后用不一样的方式在数组之间移动数据,比如,用arrayA的行去填充arrayB的列,再加上1的偏移值。这么做足以让程序员抓狂。

存取方法和公共变量同时用上。这样一来,你无需调用存取器的开销就可以修改一个对象的变量,还能宣称这个类是个”JavaBean”。程序员可能会试图添加日志函数来找出改变值的源头,但这一招会让他迷惑不已。

在单独源文件的函数定义中,确保所有API函数至少包装了6-8次。你也可以使用#defines的快捷方式来实现对这些函数的包装。

这种技术具有驱动程序包的任何用户或记录器以及维护程序员分心的附加优点。创建十几个重载变量的同一方法,只有最细微的细节不同。我认为是奥斯卡王尔德,观察到KamaSutra的位置47和115是相同的,除了在115的地方女人的手指是交叉的。包的用户必须仔细地阅读长列表的方法,以找出使用哪个变量。该技术还使文档膨胀化,从而确保其更可能过时。如果老板问你为什么这样做,解释它只是为了方便用户。再次为了充分达到效果,克隆任何共同的逻辑,坐下来等待它的副本逐渐失去同步。

交换参数,把这样的函数drawRectangle(height,width),变成drawRectangle(width,height),并且除了名字之外不做任何改变。几个版本后,再换回来。这样维护人员根本不能通过普通的函数调用就快速发现方法已经被改变了。一般而言这是是留给读者作练习用的。

与其将参数用于单个方法,不如尽可能的创建多个单独的方法。举个例子,有一个方法是setAlignment(intalignment),如果alignment是枚举常量,有着left,right,center三个实例,那就创建三个方法setLeftAlignment,setRightAlignment,和setCenterAlignment.当然,为了效果最大化,你必须拷贝这些相同的代码逻辑使得它们难以同步。

尽可能的多写静态变量。如果在程序里,你不需要某个类多于一个的实例,别人也不会需要的。重复,如果别人抱怨,告诉他们这样存取速度更快。

学习嘉吉猜想(我觉得嘉吉是男的)"任何设计问题都可以通过增加中间层次解决,即使是中间层太多的问题"。分解面向对象的程序,直到搞不清究竟哪个对象负责改变程序状态为止。更好的是,安排所有的回调事件激活都要通过指针森林,就是那个包含了程序可能用到的所有函数指针的森林。激活遍历森林的一个副作用就是,在释放对象引用计数指针之前就创造一个深拷贝,即使实际上不需要深拷贝。

全堆一块

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

鄙视Java的接口吧!如果你的上级工程师抱怨,就告诉他们Java强迫你在两个不同的类(同样实现了这个接口)之间"剪切"代码,这样他们就知道有多难维护了。而且,像JavaAWT设计者一样工作,写一大堆功能,但只能被子类调用,再在方法里加上一大堆"instanceof"类型检查.这样,如果有人想重用你的代码,他们必须继承你的类。如果他们想重用你的属于两个不同的类的代码-真不走运,他们不能同时继承两个类!如果非得用接口,起一个万能的名字,比如ImplementableIface。另一个学术界珍珠是给类命名为被实现接口的名称+“Impl”。这也能提供巨大的帮助。e.g.让类实现Runnable接口。

永远不用布局管理器。这样当维护程序员添加一个或更多组件时,他必须手动调整屏幕上每一个组件的绝对位置。如果你老板强迫你使用布局管理器,就用个大的GridBagLayout,然后硬编码网格坐标。

如果你必须为其他程序员编写类,在你的类里添加环境检查代码(getenv()inC++/System.getProperty()inJava),放在静态代码块里,然后搞到所有需要的参数,而不是通过构造器接收。这种方法的优点是初始化工作发生的尽可能早,甚至与类文件被加载一样早,甚至比类实例化更早,甚至在main()之前就被执行了。换句话说,在你的类文件读取之前,程序的其余部分根本不能更改这些参数-就是说用户最好把他们自己的环境变量设置的和你设置的一样!

避免任何形式的表驱动逻辑。开始看上去再简单不过了,但是最终使得用户会对校对不寒而栗,甚至是简单的修改表格。

在Java里,所有私有数据类型是以传值的方式传参,所有传递的参数真的是只读的。调用者可以随便修改参数,被调用者不会受到任何影响。相对的,被传递过来的对象是可读写的。引用是按值传递的,但被引用的对象是同一个。调用者可以对传过来的对象字段做任何想做的事。不用记录方法事实上有没有修改传递过来的对象。给你的方法起一个好名字,使它看上去仅仅是查看了对象的值,但实际上却进行了修改。

别使用异常来处理进程错误,把你的错误信息存到一个全局变量里。确保系统的每一个长循环都检查全局标签,并在发现异常时终止。再添加一个全局变量用来标识用户点击了'重置'按钮。当然了,系统里的所有主要循环也得检查第二个标签。还有些不需要终止的循环,隐藏起来吧。

如果上帝不想让我们使用全局变量,就不会发明它了。别让上帝失望,尽可能多的设置和使用全局变量吧。每个函数都应该用上几个,即使实际上不需要也行。毕竟,任何好的维护工程师都会很快发现这是一次侦探锻炼,他也会很开心的意识到,这个锻炼能测试出一个人究竟是不是优秀的维护工程师,抑或只是个新来的而已。

全局变量完全把你从指定函数参数的工作中解救出来了。利用这个优势。选择这些全局变量中的一个或多个指定其他进程要做什么。维护工程师居然愚蠢的认为C函数没有副作用。确保他们想不到你在全局变量存储结果和内部状态信息。

在C里,函数应该是幂等的(没有副作用)(幂等:同一个函数在参数相同的情况下执行结果相同)。我希望这个提示足够明显。

在循环体里,假装循环必定成功,而且会立即更新所有的指针变量。如果随后在循环过程中出现了一个异常,回退被作为循环体条件并步进的指针。

如果你需要定义一个结构体来保存数据回调,总把这个结构体命名为PRIVDATA。每个模块都应该定义它自己的PRIVDATA。在VC++,这有着让调试器混乱的优点,如果你跟踪一个叫PRIVDATA的变量,还想放在调试窗里观察,调试器会不知道你想看哪一个PRIVDATA,只好随便挑一个。

它们通常是键值对类型的。那些值将在启动时被加载。最棒的混淆技术是给java变量及键们使用只有轻微不同的名字把那些运行时也不改变的常量也放在配置文件里。参数文件变量至少需要比比一个简单变量至少多5倍的维护代码才行。

对于写不可维护的代码,面向对象编程简直是上天送来的礼物。如果你想创建一个类,有10个成员(属性/方法),那你应该考虑这种写法:写一个基类,只有一个成员,继承9层,每层添加一个成员。这样,到最后一层时,你就具有了全部的10个成员。如果可能,将每个类放到不同的文件中,这将更好的膨胀你的INCLUDE和USES语句,这样维护人员就不得不用他的编辑器同时打开比10个还多的文件。当然啦,每一次继承都得有对应的实例创建才行。

Sedulouslyeschewobfuscatoryhyperverbosityandprolixity.

(坚决避开混淆,冗长,和啰嗦)

参与网上的C混淆大赛并学习大师的一鳞半爪。

在那个世界,代码越简洁,工作方式就越诡异,你就越受人尊重。

能用两三个辅助变量解决的问题就别用一个变量解决

永不停止寻找解决常规问题的更模糊的方法。例如,别用数组将整数转换为相应的字符串,要使用类似这样的代码:

char*p;switch(n){case1:p="one";if(0)case2:p="two";if(0)case3:p="three";printf("%s",p);break;}一致性的小淘气当你需要一个字符常量的时候,可以用多种不同格式:‘‘,32,0×20,040。在C或Java里10和010是不同的数(0开头的表示8进制),你也可以充分利用这个特性

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

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

记住编程语言中所有微妙的隐式转换规则。充分利用它们。不使用图片变量(picturevariable)(inCOBOLorPL/I)也不用常规类型转换(比如sprintfinC)。确保使用浮点变量作为下标访问数组,字符作为循环计数器并对数字使用字符串函数。毕竟,所有这些操作都没问题而且还让你的代码显得很简洁。任何试图理解他们的维护者都会非常感激你,因为他们必须阅读和学习关于隐式数据类型转换的整个章节,一篇在遇到你的程序之前他们可能完全忽视的章节。

使用组合框时,在switch条件中使用整数而不是定义好的常量

只要语法允许,多用分号。举个例子:

if(a);else;{intd;d=c;};用八进制在列好的十进制的数字里夹杂一点八进制数,像这样:

array=newint[]{111,120,013,121,};类型转换在类型转换时java提供了很多混淆机会。一个简单的例子:如果你要将一个Double转换为字符串,曲折一点,使用newDouble(d).toString()就比直接Double.toString(d)要好.你可以的,当然可以,更曲折一点!避免使用任何大受好评的类型转换方式。转换过程中在堆里再扔点临时对象就更好了。

尽可能的嵌套。好的程序员能在一行写10组(),能在一个方法里写出20组{}。C++程序员还有更强大的能力,除了底层代码嵌套之外,还能做预处理嵌套。如果程序印刷出来开始和结尾都不在一页上那就更属于额外的餐后甜点了。只要有可能,就把ifs嵌套转换成[]三元表达式。如果它们跨越几行,就更完美了。

如果你有一个有100个元素的数组,尽可能多地在程序中直接写100。别写静态常量,也别引用myArray.length.为了使改变这个常数更加困难,直接写50而不是100/2,或者99而不是100-1.你可以进一步伪装100,把a>100换成a==101,把a>=100换成a>99。考虑页面大小,总行数由ofx行header,y行body,andz行footer组成,通过时而组装,时而分写的方法,你可以更好的利用这些东西来制作陷阱。

这由来已久的技术在拥有两个不相干的数组恰好都有100个元素的程序中尤其有效。如果维护程序员擅自改变其中一个数组的长度,他必须解读程序中所有使用100进行迭代的for循环,以分辨出究竟哪个循环依赖了这个数组。几乎可以肯定,他至少会造成一个错误,希望多年后不会出现另一个。

C编译器会把myArray[i]转换成*(myArray+i),这当然*(i+myArray)等价,也就与i[myArray]等价。专家知道怎么向好的方面使用这项技术。但对我们而言真正值得利用的,是用函数生成下标的时候:

intmyfunc(intq,intp){returnp%q;}//...myfunc(6291,8)[Array];不幸的是,这些技术只能用于本地化的C类,java不让。

把尽可能多的代码打包成单行。这节省了临时变量的开销,还能通过消除换行符和空格使源文件变短。提示:移除操作符周围的所有空白。好的程序员经常会困扰于一些编辑器强加的255字符行长度限制。好处是长行让那些不能阅读6号字的程序员必须拖动滚动条才能看完这一整行。

在不需要异常的语句里使用异常。一般使用ArrayIndexOutOfBoundsException来终止循环就很好。用异常还能帮助你避免方法正常返回结果。

标题说明一切(titlesaysitall)

了解语言在新闻组里各种各样关于花式的位运算魔法代码的争论e.g.a=a++;orf(a++,a++);在你的库里随便放点吧.在C里,后果就是前/后自减代码,比如

*++b(*++b+*(b-1))0就不是由语言规范定义的,每个编译器都可以自由地按不同的顺序进行解析.这使它更加致命。同样,好好利用C和Java移除所有空格后复杂的解析规则.

完全遵守nogoto,noearlyreturns的建议,也别写标签,这样和至少5层深的if/else嵌套再搭配不过了。

除非语法要求,否则不用{}包围你的if/else块。如果你有多层混杂的if/else,再配上误导性的缩进,甚至专家级维修程序员都会中招。为了让这项技术效果最大化,用Perl.你甚至还能在代码行尾加上ifs,简直神乎其技。

永远不要低估你可以通过制表符来造成多少破坏,只需要用制表符代替空格,尤其是公司没规定一个制表符代表几个空格的时候。让字里行间充满制表符,或者使用工具来完成空格对制表符的转换也很好。

在某些数组位置使用特殊值作为标签。一个好的例子是某个数组的[3][0]元素被用于产生一个齐次坐标系统。

如果需要给定类型的多个变量,定义一个数组就行了,然后用下标访问。只有你知道每个下标的意义并且不在文档里记录。也不要费心去用#define把下标定义成常数。每个人都应该知道全局变量widget[15]是取消按钮.这种方法一种最新的变体是在汇编中使用绝对地址。

if(a)if(b)x=y;elsex=z;宏预处理器它提供了巨大的混淆机会,这项技术的关键点是几层深的嵌套宏展开,你应该把宏命令分散在各个不同文件中,将可执行代码换成宏然后分散到各个*.cpp文件中(即使是那些从来不用的宏),这将使代码变更的编译需求数量最大化。

byte[]rowvector,colvector,matrix[];类似于:

byte[]rowvector;byte[]colvector;byte[][]matrix;隐藏错误恢复代码使用嵌套将函数的错误恢复方法尽可能远离调用的位置。一个简单的例子就是设置了10或者12级的嵌套:

让维护程序员去猜测你使用的方法和类究竟来自那一个包。不要这样写:

importMyPackage.Read;importMyPackage.Write;应该用:

在任何情况下,决不能允许在屏幕上出现多于一个函数或过程的代码。实现这一点很简单,使用这个方便的技巧就行:空行通常用于分离代码的逻辑块。每行本身都一个逻辑块.在每行之间放上空行。不要在代码的结尾写注释。把它放在这一行的上面.如果你被迫在行尾写注释,在整个文件中选择最长的代码行,加上10个空格,然后将所有其他行注释的开头与该列对齐。

方法顶部的注释需要使用模板,而且最少包含15行,包括很多的空行。下面是具体的例子:

/*/*ProcedureName:/*/*Originalprocedurename:/*/*Author:/*/*Dateofcreation:/*/*Datesofmodification:/*/*Modificationauthors:/*/*Originalfilename:/*/*Purpose:/*/*Intent:/*/*Designation:/*/*Classesused:/*/*Constants:/*/*Localvariables:/*/*Parameters:/*/*Dateofcreation:/*/*Purpose:*/在文档中放置这么多冗余信息的这项技术,可以让它很快的过时,并有助于负责维护的程序员傻到去相信它。

Idon'tneedtotestmyprograms.Ihaveanerror-correctingmodem.

(我不需要测试我的程序。我有一个错误纠正调制解调器。)

把Bug留在程序里,留给新来的维护工程师,留给寻找有趣事情的后来者。一个完美的Bug从不留下任何可能解释本身出现位置或原因的线索,最懒,但也最有效的方法就是从来不测试你的代码.

伙计,如果程序不够快,告诉客户买台更快的机器就好了。如果你做了性能测试,就可能发现性能瓶颈,就可能需要改变算法啊,就可能需要重构整个产品.谁想那么做而且,在客户现场上出现的性能问题意味着一次免费体验异国情调的旅行,只要换个新镜头并准备好护照就行了。

拒绝执行代码覆盖或路径覆盖测试,自动化测试给窝囊废用的。自己找出哪些功能占你日常使用的90%,并分配90%测试到这些路径。毕竟,这个技术可能只测试你源代码的60%,直接节省了40%的测试工作量.这可以帮助你在项目后期补偿计划量表。很久以后才会有人注意到那些美妙的“营销特性”不工作。著名的大型软件公司就是这样测试代码的;你也应该这样.如果看到这里你还没走,请参阅下一项.

勇敢的程序员从不测试。太多的程序员害怕老板,害怕失去工作,害怕客户的指责邮件,害怕被起诉了。这种恐惧麻痹了热情,降低了工作效率.研究表明,消除测试阶段意味着管理人员可以提前确定上线日期,这对工作计划有着明显帮助。恐惧消失,创新和实验将开花结果。程序员的角色是生成代码,debug是帮助台和维护工程师的事,互相合作嘛。如果我们对我们的编码能力有充分的信心,那么测试就没有必要。从逻辑上看,傻瓜都知道测试解决不了任何技术问题,相对的,这只是一个情感信心的问题。对缺乏信心这个问题,一个更有效的解决方案是完全消除测试并送我们的程序员去上建立自信的课程.毕竟,如果我们选择做测试,我们就得测试程序的每个变化,但现在我们只需要送我们的程序员去上一节课来建立自信就成了,这可是惊人的成本效益啊。

如果你定义TESTING为1

#defineTESTING1这给了你一个很好的机会,插入单独的代码段,如

#ifTESTING==1#endif它可以包含一些不可或缺的代码,比如

x=rt_val;所以,如果有人把TESTING改成了0,这程序就将停止工作。只要再加上一小点想象力,不仅能做到逻辑混乱,还能让编译器也混乱。

Philosophyisabattleagainstthebewitchmentofourintelligencebymeansoflanguage.

电脑语言正在变得越来越愚蠢,使用那些新的静态语言简直反人类。坚持使用你能侥幸找到的最老的语言,如果可以的话,用八进制的语言(像HansundFrans一样,我不是娘娘腔的男人;我很有男子气概的,我用漆金线连接IBM单元记录设备(穿孔卡片),并用手在纸带上打孔实现编码),汇编不够格,FORTRAN和COBOL也不够格,C和BASIC不不够格,C++也不够格.,全都不够格。

这些技术大约有20%不能用于Ada,拒绝它吧。如果老板强迫你用,坚持回答别人也不用,并且指出它会让你的大量工具失效,比如lint和plummer,这可都是能有效利用C缺陷的法宝啊。

把所有公共的效用函数转换成asm.

确保所有重要的库函数用QBASIC编写,然后简单的写一个asm包装器就能处理大->中内存模型映射。

在程序里随机写点汇编多有意思啊,几乎没有人懂,即使是几行也能冷却程序员的维护妄想。

如果你有被C调用的汇编模块,尽可能多的使用它,即使是最微不足道的目的,也要确保充分利用了goto,bcc或者其他迷人的汇编习俗。

不适用Abundance编码,也别用任何其他语言的编码工具,它们从一开始就被设计的主要目就是使维护工程师的工作变简单。同样,避免使用Eiffel或者Ada,它们从一开始就是为了在项目进入生产之前捕捉bug而设计的。

__Hellisotherpeople.__(地狱就是别人)

这里有很多灵光一现的点子,能让那些喋喋不休的维护工程师变的沮丧,能击溃你的老板阻止你写不可维护代码的妄想,甚至煽动起一场关于每个人在库中的代码格式应该是什么样子的争论.

如果你的老板认为他/她自己20年的FORTRAN编程经验足以指导现代编程,严格遵守他/她的所有建议。这样,老板会信任你。这对你的职业生涯有益,而且你会学到很多新的方法来混淆程序代码。

微妙是一件有趣的事,有时锤子比别的工具更容易微妙。试试微妙的注释,创造一个类,有着类似FooFactory的名字,注释包含对GoF创造模式的引用(最好超链接到假的UML设计文档),但是却在对象创造的过程中什么也不做。就这样尽情的玩弄维护者的预期吧。更微妙的是,让java类有一个受保护的构造器和类似于Foof=Foo.newInstance()的方法,返回新实例,而不是别人预期的单例.这样微妙的机会是无限的。

你总是想写系统级别的代码,现在,机会来了。忽略标准库,写你自己的。放在简历上也好看。

总是使用你自己的,独一无二的,BNF无关的命令语法。不要试图在注释里为合法和非法命令提供配套的解释.那被证明了完全缺乏学术严谨性,铁路运行时刻表从来都不准确。确保没有对从中间过渡符号(真的有合适的意思)到最终的符号(你最终键入的)演变做明显的解释。永远也不使用字体,颜色,加粗或其他任何视觉线索来帮助阅读者区分这两者。在你自己的BNF规范里使用与程序完全相同的标点符号,这样阅读者就搞不清在你的BNF规范里(...),[...],{...}或者"..."究竟是命令的一部分还是提供线索表明那些语法元素是强制性的,可重复的还是可选的。最终,如果他们太愚蠢以至于理解不了你的BNF规范,那他们当然就对你的程序无计可施了。

每个人都知道调试动态存储复杂而费时。改造你的存储分配器而不是确保每一个类都没有内存泄露。强迫你的用户定期重启系统来清理堆而不是释放内存。系统重启是一件很简单的事--比修复所有的内存泄露漏洞简单多了。只要用户记得定期重启系统,就不会发生运行时堆空间溢出。想象一下,这能改变他们“一次部署”的策略!

让你脑残的基本编程方法

给数据库表取一到二个字符长度的名称。最好是跟它毫无关系的已有表的表名类似。

混合各种外部连接语法,让大家看着头皮发麻。

"优化"JavaScript代码要充分利用一个函数可以在调用者的范围内访问所有本地变量的特性。

将如下定义:

DimCount_num$,Color_var$,counter%VisualBasic疯狂如果从一个文本文件读取15个字符,那么可以使用如下方法来让事情变得复杂:

ReadChars=.ReadChars(29,0)ReadChar=trim(left(mid(ReadChar,len(ReadChar)-15,len(ReadChar)-5),7))IfReadChars="alongsentancewithoutanyspaces"Mid,14,24="withoutanys"andleft,5="without"Delphi/Pascal不准使用函数和过程。使用label/goto语句,然后在代码中乱跳。这足以让他人在跟踪代码时发疯。另外就是让代码在不经意间来回跳跃,没错,就是这个窍门。

在一行非常非常长的代码结尾使用if和unless语句。

LISP一个非常梦幻般的语言,用来写不可维护代码。看看下面的这些令人困惑代码片段:

(lambda(*<8-]=*<8-[=)(or*<8-]=*<8-[=))(defun:-](<)(=<2))(defun!(!)(if(and(funcall(lambda(!)(if(and'(<0)(

lcx=TYPE('somevariable')lcx的值将是'U'或者undefined.但是!如果你给这个变量一个范围并在之后进行赋值,就会产生一个逻辑错误,嗯?!

LOCALlcxlcx=TYPE('somevariable')lcx的值现在是'L'或者logical.而后被定义为FALSE的值。想想一下把这个东西用来写不可维护的代码!

对于试图用行调试工具追踪来看懂你的代码的人,简单的一招就能让他狼狈不堪,那就是把每一行代码都写得很长。特别要把then语句和if语句放在同一行里。他们无法设置断点。他们也无法分清在看的分支是哪个if里的。

在工程方面有两种编码方式。一种是把所有输入都转换为公制(米制)计量单位,然后在输出的时候自己换算回各种民用计量单位。另一种是从头到尾都保持各种计量单位混合在一起。总是选择第二种方式,这就是美国之道!

CANI是ConstantAndNever-endingImprovement的缩写,持续改进的意思。要常常对你的代码做出“改进”,并强迫用户经常升级—毕竟没人愿意用一个过时的版本嘛。即便他们觉得他们对现有的程序满意了,想想看,如果他们看到你又“完善“了它,他们会多么开心啊!不要告诉任何人版本之间的差别,除非你被逼无奈—毕竟,为什么要告诉他们本来永远也不会注意到的一些bug呢?

在两个版本之间,你能做的变更自然是多多益善。你不会希望用户年复一年地面对同一套老的接口或用户界面,这样会很无聊。最后,如果你能在用户不注意的情况下做出这些变更,那就更好了—这会让他们保持警惕,戒骄戒躁。.

与常规header不同。这具有双重优点,它要求在每个文件中维持的参数数据类型的改变,并且避免编译器或链接器将检测到类型不匹配的情况。当从32->64位平台移植时,这非常有用。

写无法维护代码不需要多高的技术水平。喊破嗓子不如甩开膀子,不管三七二十一开始写代码就行了。记住,管理层还在按代码行数考核生产率,即使以后这些代码里的大部分都得删掉。

一招鲜吃遍天,会干什么就吆喝什么,轻装前进。如果你手头只有一把锤子,那么所有的问题都是钉子。

有可能的话,忽略当前你的项目所用语言和环境中被普罗大众所接受的编程规范。比如,编写基于MFC的应用时,就坚持使用STL编码风格。

把常用的true和false的定义反过来用。这一招听起来平淡无奇,但是往往收获奇效。你可以先藏好下面的定义:

#defineTRUE0#defineFALSE1把这个定义深深地藏在代码中某个没人会再去看的文件里不易被发现的地方,然后让程序做下面这样的比较:

if(var==TRUE)if(var!=FALSE)某些人肯定会迫不及待地跳出来“修正”这种明显的冗余,并且在其他地方照着常规去使用变量var:

if(var)还有一招是为TRUE和FALSE赋予相同的值,虽然大部分人可能会看穿这种骗局。给它们分别赋值1和2或者-1和0是让他们瞎忙乎的方式里更精巧的,而且这样做看起来也不失对他们的尊重。你在Java里也可以用这一招,定义一个叫TRUE的静态常量。在这种情况下,其他程序员更有可能怀疑你干的不是好事,因为Java里已经有了内建的标识符true。

在你的项目里引用了功能强大的第三方类库,但是却没有去使用他们。虽然有了实践,你却完全不理会那些好的工具,却把那些没用的工具写在你的简历中的“其他工具”部分。

假装不知道那些在你的开发工具中直接引用的类库。在VisualC++的代码中忽略MFC或者STL的存在,却使用手工编码实现所有的字符串和数组;这将有助于保持您的指针技能很牛逼,并且将会自动使你尝试扩展代码成为不可能。

保证它是如此的精巧以至于没有任何维护人员能让自己的修复通过编译。保密使用SmartJ来编译脚本已经过时了的事实。同样的,对javac编译器也是可用类的事实保密。即使面临死亡的痛苦,也别告诉别人编写和维护一个小而快的java程序来寻找文件并调用sun.tools.javac.Main完成编译是如此的简单。

用脚本复制法在无序的复数文件夹中完成代码复制就行了!用不着任何花哨的源代码控制系统来控制代码分支,这样让你的继任者即使只想找出自己需要的是哪个版本的DoUsefulWork()也变得很难。

把所有的代码放到makefile里,你的继任者一定会为你的壮举留下深刻印象,因为你只用一个makefile就能生成批处理文件来完成所有头文件的导入和工程构建!这样他们就弄不清任何改动会带来什么影响,也搞不懂怎样才能把工程迁移到IDE中。使用过时的工具来让效果最大化,比如早就完蛋的根本没有依赖概念的NMAKE。

某些公司有严格的规定,不允许使用数字标识符,你必须使用预先命名的常量。要挫败这种规定背后的意图太容易了。比如,一位聪明的C++程序员是这么写的:

没错,你的客户会要求向上兼容性,所以你大可以放心这么做。但是请确保不会有向下兼容性问题。因为这样将会导致客户无法接受新版本,再加上一个合理的Bug修复政策(见上文),这也会导致一旦升级到一个新的版本,他们就不会再想做任何变动了。如果能想办法让老版本不能识别由新版本创建的文件,那你就会得到额外的加分(这里是反语)。那样的话,他们不仅不能读取,而且还会拒绝它,认为它不是由同一个应用所创建的文件!提示:PC版本的文字处理器就是一个很有用的例子,能让我们看清楚这种复杂的行为。

不要探究bug产生的深层原因,简单的掩饰一下推到高级线程就好,这是一个很好的智力练习,类似于三维国际象棋,能让未来的维护工程师花好几个小时来辨别究竟是低级线程产生数据时的问题还是高级线程更改变量时产生的问题。对编译器而言,这是一种很好的技术,“多路通行”!你完全可以避免在早期测试中简简单单的修复这个问题,这样在后期测试中就会变得很难修复了。幸运的话,你也用不着向那些负责前端维护的傻蛋们做什么解释。如果前端产生正确的数据反而会让后端崩溃,就更完美了。

为了使用死锁要精巧的避开同步许可。多睡眠(sleep),多测试,多利用那些(没有被volatile修饰)的全局变量。死锁比系统对象更加容易使用,而且还常见而灵活。

随机的使用同步控制,即使瞎选的地方不需要。我曾经在一段完全不可能被第二个线程调用的代码块中发现了同步控制(critical)代码块,我批评了代码作者,可他居然信誓旦旦的告诉我critical能证明这段代码,wow,关键!(critical用于同步控制,但英文意思是关键)

如果你的系统包含NT设备驱动,在活动期间让应用程序申请I/O缓冲区并加锁,事后再解锁。如果过早终止该缓冲区锁定,程序将导致NT崩溃.但是在客户端站点没有人能改变设备驱动程序,所以他们没有选择。

将脚本命令语言加入到您运行时编译字节的客户机/服务器应用程序中

如果你发现了编译器或者解释器的Bug,请确保这种行为对让你的代码正常工作有必要。你不应该换另外一个编译器,其他人也不应该。

下面是一个研究生写的真实的例子,我们来看看他将不同的实现记录打包到同一个C函数中:

让每个方法的代码尽可能的多,希望你从来没有写过任何少于1000行的代码,当然也可以使用深度的嵌套:)

确保让他们找不到你的某个关键文件,这个最佳做法是在include语句中,例如,在你的主模块里有这样的代码:

#includestdcode.h这个文件当然必须得有,但是在这个文件里我们可以再玩一下:

#include"a:\\refcode.h"然后refcode.h就找不到罗。。。

你至少得有一个变量,到处都有对这个变量进行赋值,但是却从来不会被用到。不幸的是,如果反过来做的话,很多先进的编译器会阻止你这样做,但你仍然可以在C或者C++代码中这样玩。

设计语言的人是编写编译器和系统类的人。他们设计时自然使他们的工作容易和数学上显得优雅。然而,每个编译器有10,000个维护程序员。日常维护程序员在语言设计上绝对没有话语权。然而,他们编写的代码的总量使编译器中的代码质量变差。

这种精英思想结果的一个例子是JDBC接口。它使得JDBC实现者工作容易,但是对于维护程序员来说是一个噩梦。它比30年前SQL出来的FORTRAN接口更笨拙。

想象一个会计师作为客户坚持使用word程序处理他的总账目。你最好尽力去说服他,他的数据应该结构化。他需要通过跨域检查进行验证。如果数据存储在数据库,包括受控同步更新,他可以处理更多的数据,这样你就可以说服他。

想象一下,软件开发者是一个客户。他坚持用文本编辑器维护他的所有数据(源代码)。他还没有利用字处理器的颜色,类型大小或字体。

想想如果我们开始将源代码存储为结构化数据可能会发生什么。我们可以以许多替代方式查看相同的源代码,例如作为Java,作为NextRex,作为决策表,作为流程图,作为循环结构骨架(删除细节),作为具有各种级别的细节或删除注释的Java,作为变量和方法调用的高亮显示的Java或者作为具有关于参数名称和/或类型的生成的注释的Java。我们可以在2D中显示复杂的算术表达式,TeX和数学家的方式。您可以看到带有更多或更少括号的代码(取决于您对优先规则的感觉)。括号巢可以使用不同的大小和颜色来帮助眼睛匹配它们。透明覆盖集的更改,您可以选择删除或应用。您可以实时观察您的团队中的其他程序员,在不同的国家工作,修改您也在使用的类中的代码。

你可以利用现代屏幕全彩色的能力得到潜意识的线索,例如通过自动给每一个包或类分配一部分频谱,使用柔和的色调作为任何引用该类的方法或变量的背景。你可以给任何标识符的定义加粗显示,使其脱颖而出。

你也许会问什么方法或构造函数会产生一个X类型的对象?什么方法将接受一个X类型的对象作为一个参数?在代码中的这一点上,哪些变量是可访问的?通过单击方法调用或变量引用,你可以看到它的定义,从而帮助找出一个给定方法的哪个版本实际上会被调用。你可以要求全局访问所有对一个给定的方法或变量的引用,每一个被处理后,给他们打勾。你可以通过点击和按下按钮就写出大量的代码来。

THE END
1.三界奇缘想要改变造型颜色应该找谁三界奇缘想要改变造型颜色应该找谁 三界奇缘题目:想要改变造型颜色应该找谁 想要改变造型颜色应该找谁 配色师https://www.18183.com/mhxy/201506/337895.html
2.实验中学启梦文学社第五十一期《我们都是追梦人》为呈现她美轮美奂造型 和微风追逐 那是一把泥土 不骄傲,不炫耀 让自己奉献给森林万物 春天里编织五彩的梦 秋天收获金黄的梦 那是一艘小船 逆水行舟用力撑 绕暗礁 峰回路转势不改 为到达彼岸毫不懈怠 那是一只雄鹰 满载誓言 把青春交给风浪搏击 揣着希望和梦想 ...https://www.meipian.cn/1y7sy1t6
3.梦幻西游手游飞燕女全套染色方案随着新角色飞燕女的出场,不少玩家都按耐不住开始要染色改变造型了,梦幻西游手游中飞燕女染色方案展示及所需消耗的彩果和花豆,完整的飞燕女染色方案集锦,你会喜欢哪一种~ 4399梦幻西游手游游戏截图:点击进入 ┃染色方案1 先来看看原始形态没染色飞燕女是怎么样的? https://news.4399.com/gonglue/mhxyou/jietu/xq/m/569601.html
1.美术梦幻的城堡教案(精选10篇)梦幻中的城堡 施教时间: 11月22日—11月23日 领域: 造型表现,欣赏评述 课时: 1课时 教学目标 1.通过学生之间的交流及有创意的角色模仿,启发学生对城堡展开想象。通过对动画片的欣赏,开拓学生思路。 2.指导学生用各种材料采用折,剪,镂,绘制等方法,创作出富有个性特点和形式感的新颖城堡。 https://www.ruiwen.com/meishujiaoan/5651197.html
2.梦幻西游改变角色造型梦幻西游召唤兽进阶成原造型大家在梦幻中有很多的方式是可以改变召唤兽的外形的,变异类型的召唤兽洗练的时候就有可能会改变外形的颜色,其他的召唤兽需要使用幻色丹才能够改变召唤兽的颜色,而且梦幻西游中绝大多数的召唤兽都是有饰品的,穿戴饰品也可以改变召唤兽的造型。 在梦幻西游中,召唤兽进阶成原造型是指通过特定的条件和材料,将召唤兽的外...https://www.9game.cn/menghuanxiyou/9889027.html
3.想在加州变网红你只需要找堵墙(内容超丰富)由无数彩色长条组成的色带帘门(天知道有多少种颜色,太丰富了),可以随意摆pose,尽情发挥创意拍照,拍个小视频也不错。 图片来源于网络 布满彩色纸片的房间是个亮点,游客可以向天空抛洒彩色纸片以形成七彩雨的景色。但由于房间太火了,人比较多,唯一的难点是找个不被打扰的角落。 https://m.mafengwo.cn/gonglve/ziyouxing/243733.html
4.梦幻蓝调:海天之色,深浅皆美文物中的中国传统色足底阴刻楷书“乾隆年制”四字款。瓶口沿和足部套碧绿色玻璃,颈、腹部饰白、蓝、红三色相间的条带纹,通体螺旋缠绕,其颜色的相间规律为白、红、白、蓝。此瓶颜色纯正,质地洁净,造型雅致,纹饰流畅活泼,如行云流水,为本来静止的器物注入了勃勃生机,是不可多得的清代玻璃珍品。https://i.ifeng.com/c/8Zb6ldRJkr5
5.github.com/kaluojushi/sodaguide/commit/2b8fa9ba2233a71836de...+ +生命各种选择题 改变或留在原地 +金银斧头 不能都拥有 哎~~~ + +我在干什么 我是谁 我在哪里 + +喔唉呀唉呀 唉呀呀 灵魂出窍这个东西 +喔唉呀唉呀 唉呀呀 泡一杯人蔘压压惊 +喔唉呀唉呀 唉呀呀 昨日有如梦幻泡影 +喔唉呀唉呀 唉呀呀 今日一键重来开机 + +虚虚实实虚虚 八卦在打...https://github.com/kaluojushi/sodaguide/commit/2b8fa9ba2233a71836de4aa07df5f62698e4efb7.patch
6.雾霾天气怎么拍摄室内人像创作的11个题材详情介绍摄影教程《看新晋美女摄影师如何借造型改变拍照风格》 摄影师Nicole的室内主题人像作品——《绽放的笑容与带刺的荆棘》。反差极大的造型与场景,夸张的表情,在梦幻的画面背后,希望也能让看客有所思。 对于现在很多自由摄影师来说,简单的道具和场景很方便,也很节约,想要拍出好看的作品就必须在主题和造型上下功夫。 https://www.jb51.net/sheying/178635.html