如何编写不可维护的代码

2020-08-31 06:11:32

PermalLink GitHub是5000多万开发人员的家园,他们一起工作,共同托管和审查代码、管理项目和构建软件。

报名。

为了在Java编程领域创造就业机会,我传递了大师们关于如何编写非常难以维护的代码的这些技巧,这样在您之后的人将需要数年时间才能进行最简单的更改。此外,如果您虔诚地遵循所有这些规则,您甚至可以保证自己终生受雇,因为除了您之外,没有人会对维护代码抱有地狱般的希望。话又说回来,如果你虔诚地遵循所有这些规则,即使你也不能维护代码!

你不会想做得太过分的。您的代码看起来不应该无可救药地不可维护,就是这样。否则,它将面临被重写或重构的风险。

要挫败维护程序员,你必须了解他的想法。他有你的大项目。他没有时间把它全部读完,更不用说理解了。他希望迅速找到地方进行改变,做到并离开,并且不会因为改变而产生意想不到的副作用。

他通过卫生纸管道查看您的代码。他一次只能看到您程序的一小部分。你要确保他这样做永远看不到大局。您想让他尽可能难以找到他正在寻找的代码。但更重要的是,你想让他尽可能尴尬地安全地忽略任何事情。

程序员被惯例所迷惑,陷入了自满的境地。但每隔一段时间,通过巧妙地违反惯例,您就会强迫他用放大镜阅读您的代码的每一行。

您可能会认为,每种语言功能都会使代码无法维护--事实并非如此,只有在正确使用的情况下才会这样。

当我使用一个词时,汉普蒂·邓普蒂用相当轻蔑的语气说,它的意思就是我选择这个词的意思--既不多也不少。

编写不可维护的代码的大部分技能都是命名变量和方法的艺术。它们对编译器来说一点也不重要。这给了您很大的自由来使用它们来迷惑维护程序员。

买一本婴儿命名书,你就永远不会记不清变量名了。弗雷德是个很棒的名字,而且很容易打字。如果您正在寻找易于键入的变量名称,如果您使用DSK键盘键入,请尝试asdf或aoeu。

如果将变量命名为a、b、c,则不可能使用简单的文本编辑器搜索它们的实例。此外,没有人能够猜到它们是用来做什么的。如果有人甚至暗示要打破自FüRTRAN以来尊重的使用i、j和k为变量编制索引的传统,即用ii、jj和kk替换它们,请警告他们西班牙宗教裁判所对异端所做的一切。

如果必须使用描述性变量和函数名称,请将它们拼写错误。通过在某些函数和变量名称中拼写错误,而在其他名称(如SetPintleOpening和SetPintalClosing)中正确拼写,我们实际上否定了grep或IDE搜索技术的使用。它工作得出奇地好。通过在不同的影院/影院拼写Tory或Tori来增加国际风味。

在命名函数和变量时,要大量使用诸如it、Everything、Data、Handle、Stuff、DO、ROUTING、PERFORM等抽象词语和数字,如routineX48、PerformDataFunction、doit、HandleStuff和do_args_method。

使用缩写词来保持代码的简洁性。真正的男人从不定义缩略语;他们从基因上理解它们。

要打破无聊,可以使用同义词库查找尽可能多的替代词汇,以表示相同的操作,例如,显示、显示、呈现。含糊地暗示着有一些微妙的不同之处,而这些地方根本就不存在。但是,如果有两个相似的功能有重要区别,请始终使用同一个词来描述这两个功能(例如,打印意味着";写入文件";,";将墨水放在纸上";和";在屏幕上显示";)。在任何情况下,都不要屈服于要求编写一个明确定义了特殊目的项目词汇的词汇表。这样做将违反信息隐藏的结构化设计原则,这是不专业的。

VMS脚本跟踪从各种Vaxen;返回的统计数据。世界语、克林贡语和霍比特语就是符合这些目的的语言。对于假世界语复数oj,请加上Oj。你们将为世界和平尽自己的一份力量。

在单词中间随机大写音节的第一个字母。例如,ComputeRasterHistoGram()。

只要语言规则允许,就给类、构造函数、方法、成员变量、参数和局部变量起相同的名称。对于额外的点,请在{}块内重用局部变量名。目标是迫使维护程序员仔细检查每个实例的范围。特别是在Java中,让普通方法伪装成构造函数。

第二个“Sí”实际上是“I-急性”的地方。只有一个简单的文本编辑器,几乎不可能区分重音标记的倾斜。

如果编译器只区分名称的前8个字符,那么可以改变结尾,例如在一种情况下是var_unit_update(),在另一种情况下是var_unit_setup()。编译器会将两者视为var_unit。

随机地穿插两种语言(人或计算机)。如果你的老板坚持让你使用他的语言,告诉他你可以用自己的语言更好地组织你的想法,或者,如果这不起作用,就声称存在语言歧视,并威胁要起诉你的雇主,要求他们赔偿一大笔钱。

扩展的ASCII字符完全可以用作变量名,包括?、-和ñ字符。如果不在简单的文本编辑器中复制/粘贴,几乎不可能键入它们。

使用外语词典作为变量名称的来源。例如,使用德语Punkt表示POINT。没有您扎实的德语掌握,维护程序员将享受破译含义的多元文化体验。

这让读者感到困惑,因为他们很难将这些词的情感内涵与他们试图思考的逻辑分开。

这个技巧在Ada中尤其有效,Ada是一种不受许多标准混淆技术影响的语言。最初给您使用的所有对象和包命名的人都是白痴。不要试图说服它们进行更改,只需使用重命名和子类型将所有内容重命名为您自己设计的名称即可。一定要在里面留下一些对旧名字的引用,以此作为粗心大意的陷阱。

切勿将i用于最内层的循环变量。使用除i之外的任何东西。可以将i自由地用于任何其他目的,特别是用于非int变量。类似地,使用n作为循环索引。

忽略Sun Java编码约定,毕竟Sun确实是这样做的。幸运的是,当您违反它们时,编译器不会告发您。我们的目标是想出只在大小写上有细微差别的名称。如果您被迫使用大写约定,您仍然可以在选择不明确的地方进行颠覆,例如同时使用输入文件名和输入文件名。发明你自己复杂得令人绝望的命名约定,然后斥责其他所有人没有遵循它们。

使用小写l表示长常量。例如,10L比10L更容易被误认为101。禁止任何明显消除uvw、ww、gq9、2z、5s、IL17|!j、oO08、`';";、;;、.、m nn rn和{[()]}歧义的字体。要有创造力。

在模块A中声明一个全局数组,在模块B的头文件中声明一个同名的私有数组,这样看起来它就是您在模块B中使用的全局数组,但它不是。在注释中不要引用此重复项。

通过以相互矛盾的方式回收变量名,尽可能混淆作用域的使用。例如,假设您有全局变量A和B,以及函数foo和bar。如果您知道变量A将定期传递给foo,将变量B定期传递给bar,请确保将函数定义为函数foo(B)和函数bar(A),以便在函数A中将始终引用为B,反之亦然。有了更多的函数和全局变量,您可以创建大量相互矛盾的同名用法的令人困惑的网络。

只要作用域规则允许,就重用现有的不相关变量名称。类似地,将同一临时变量用于两个不相关的目的(声称是为了保存堆栈槽)。对于一个可怕的变体,可以改变变量,例如,为一个非常长的方法顶部的一个变量赋值,然后在中间的某个位置,以一种微妙的方式更改变量的含义,比如将它从从0开始的坐标转换为从1开始的坐标。一定不要记录这种意义上的变化。

当在变量名或方法名中使用缩写时,可以用同一个词的几个变体来打破单调乏味的状态,甚至可以偶尔手写出来。这有助于击败那些懒惰的流浪汉,他们使用文本搜索只了解程序的某些方面。考虑一下不同的拼写,作为策略的变体,例如,将International color与美国颜色和讲男子语言的kulerz混合在一起。如果您拼写出完整的名称,则只有一种可能的方法来拼写每个名称。这些对维护程序员来说太容易记住了。因为有这么多不同的方法来缩写一个单词,使用缩写,你可以有几个不同的变量,它们都有相同的明显目的。作为额外的好处,维护程序员甚至可能没有注意到它们是独立的变量。

确保每个方法做的比它的名字所暗示的多一点(或少一点)。作为一个简单的示例,名为isValid(X)的方法的副作用应该是将x转换为二进制并将结果存储在数据库中。

来自C++世界的一个命名约定是在成员前面使用m_。只要您忘了";method";也是以字母";m";开头,这应该可以帮助您将它们与方法区分开来。

为类的每个实例使用";o&34;或";obj&34;前缀,以表明您正在考虑更大的、多态的图景。

匈牙利记数法是战术核武器的源代码混淆技术;使用它吧!由于大量的源代码被这个习语污染,没有什么能比一次精心策划的匈牙利符号攻击更快地杀死维护工程师了。以下小贴士将帮助您破坏匈牙利记法的原意:

在C++和其他直接强制变量常量的语言中,坚持使用";c";for const。

找出并使用在你当前语言之外的语言中有意义的匈利语疣。例如,坚持使用PowerBuilder l_和a_{local and argument}作用域前缀,并在编写C++代码时始终使用VB风格,即每个控件类型都有一个匈牙利瘤。尽量忽略这样一个事实,即大量明显可见的MFC源代码没有使用匈牙利语的疣作为控件类型。

总是违反匈牙利原则,即最常用的变量应该携带最少的额外信息。通过上面概述的技术并坚持每个类类型都有一个自定义的瘤前缀来实现这一目的。永远不要让任何人提醒你,没有疣告诉你某件事是一个类。这条规则的重要性怎么强调都不过分,如果您不遵守它的原则,源代码中可能会充斥着具有较高元音/辅音比的较短变量名。在最坏的情况下,这可能会导致混淆的完全崩溃,并在代码中自发地重新出现英语符号!

公然违反匈牙利式的概念,即必须为函数参数和其他高可见性符号指定有意义的名称,但是匈牙利类型的缺点本身就是极好的临时变量名。

坚持在你的匈牙利疣上使用完全正交的信息。考虑这个真实的示例a_crszkvc30LastNameCol。一组维护工程师花了近3天的时间才弄清楚,这个庞然大物变量名描述的是一个常量、引用、函数参数,该参数保存的信息来自名为LastName&34;表的主键的VAXERR[30]类型的数据库列。当恰当地结合所有变量都应该是公共变量的原则时,该技术可以立即使数千行源代码作废!

充分利用人脑只能同时容纳7条信息的原则。例如,按照上述标准编写的代码具有以下属性:

传递三个参数并分配结果的单个函数调用携带29条类型和名称信息。

寻求改进这一优秀但过于简练的标准。推荐一周中5个字母的日期前缀,以帮助隔离Monam和FriPM上编写的代码,从而给管理层和同事留下深刻印象。

即使是中等复杂的嵌套结构也很容易压倒短期内存,特别是当维护程序员无法在屏幕上同时看到每个块的开始和结束时。

匈牙利符号中的一个后续技巧是";更改变量类型,但保持变量名称不变";。从Win16迁移时,这几乎都是在Windows应用程序中完成的:

其中的w值暗示它们是单词,但它们实际上指的是长整型。在Win64迁移中,这种方法的真正价值显而易见,参数将是64位宽,但旧的w和l前缀将永远保持不变。

如果您必须定义一个结构来保存回调数据,请始终将该结构称为PRIVDATA。每个模块都可以定义自己的PRIVDATA。在VC++中,这有混淆调试器的优点,因此如果您有一个PRIVDATA变量并试图在监视窗口中展开它,它不知道您指的是哪个PRIVDATA,所以它只选择一个。

使用常量名称,如LancelotsFavouriteColour而不是Blue,并为其指定十六进制值$0204FB。颜色看起来与屏幕上的纯蓝色相同,维护程序员必须计算出0204FB(或使用一些图形工具)才能知道它看起来是什么样子。只有对Monty Python和圣杯非常熟悉的人才会知道兰斯洛特最喜欢的颜色是蓝色。如果维护程序员不能从内存中引用完整的Monty Python电影,那么他或她就没有资格成为一名程序员。

漏洞浮出水面的时间越长,就越难找到。--罗迪·格林(Rody Green)。

编写不可维护的代码的大部分技能都是伪装、隐藏东西或使东西看起来不是它们的艺术。许多依赖于这样一个事实,即编译器比人眼或文本编辑器更有能力做出细微的区分。以下是一些最好的伪装技术。

包括被注释掉代码段,但乍一看似乎并非如此。

For(j=0;j<;array_len;j+=8){Total+=array[j+0];Total+=array[j+1];Total+=array[j+2];/*total+=array[j+3];*循环展开Total+=array[j+4];*速度更快。合计+=数组[j+5];*/合计+=数组[j+6];合计+=数组[j+7];}。

如果没有颜色编码,您会注意到有三行代码被注释掉了吗?

Struct/Union和tyfinf struct/Union在C中是不同的名称空间(而不是在C++中)。对于结构或联合,请在两个名称空间中使用相同的名称。如果可能,使它们几乎兼容。

Tyecif struct{char*ptr;size_t Len;}snafu;struct snafu{unsign CNT char*ptr;size_t Len;}A;

将宏定义隐藏在垃圾注释中。程序员会感到厌烦,无法读完注释,因此永远不会发现宏。确保宏用一些奇怪的操作替换看起来完全合法的赋值,这是一个简单的示例:

使用DEFINE语句组成简单注释掉其参数的组合函数,例如:

这样,全局搜索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。如果对任意名称使用实际保留字,这会被命令处理程序或编译器拒绝,那就更好了。如果您做得很好,在您的示例中,用户将无可救药地混淆保留关键字和任意名称,但是您可以看起来是无辜的,声称您这样做是为了帮助他们将适当的用途与每个变量关联起来。

选择变量名称,使其与屏幕上显示此类变量时使用的标签完全无关。例如,在屏幕标签上,字段#34;邮政编码#34;但是在代码中调用关联的变量zip。

不使用全局重命名来使两段代码同步,而是使用同一元件的多个TYPEDEF。

因为全局变量是邪恶的,所以定义一个结构来保存您放入全局变量中的所有内容。称它为聪明的东西,比如EverythingYoullEverNeed。使所有函数都接受指向此结构的指针(调用它的句柄会使事情更加混乱)。这给人的印象是您没有使用全局变量,而是通过句柄访问所有内容。然后静态声明一个,这样所有代码无论如何都使用相同的副本。

维护程序员为了查看他们所做的更改是否会产生任何连锁效应,对名为的变量进行全局搜索。这可以通过使用同义词这个简单的权宜之计来克服,例如。

#定义文件std.h中的xxx global_var//#定义文件..\ther\substd.h中的xy_z xxx//#定义文件..\codestd\inst.h中的local_var xy_z//。

这些def应该分散在不同的包含文件中。如果包含文件位于不同的目录中,则它们特别有效。另一种技术是在每个作用域中重用一个名称。编译器可以区分它们,但是头脑简单的文本搜索者不能。不幸的是,未来十年的SCID将使这项简单的技术变得不可能,因为编辑器和编译器一样了解作用域规则。

使用非常长的变量名或类名,它们之间只有一个字符的差异,或者只有大写/小写差异。理想的变量名称对是游泳者和游泳者。利用大多数字体的失败,无法使用诸如parselnt和parseInt或D0Calc和DOCalc这样的标识符对清楚地区分ilI1|或oO08。L是一个非常好的变量名,因为随便看一下,它会伪装成常量1。在许多字体中,rn看起来像一个m。那么变量swirnrner怎么样?创建仅大小写不同的变量名,例如HashTable和Hashtable。

除了大写和下划线之外,与其他变量相似的变量有一个优点,那就是让那些喜欢通过发音或字母拼写而不是准确表示来记住名字的人感到困惑。

在C++中,使用#DEFINE重载库函数。这样看起来您使用的是熟悉的库函数,而实际上您使用的是TOT。

.