开猿节流,必备防御性编程技巧!

大家好,我是匿名用户 Q。

上次给大家分享了一篇裁员降本增笑的问题。大家对文末的 “防御性编程” 非常感兴趣,纷纷想尝试:

159a8dbe79d54f3ebbb4b6ba5f2b80d3.png

我发现国外友人已经写过一篇,有一定的价值。分享给大家。吸起来!!!以下的 “我” 均代表原作者。

原文:https://coderlmn.github.io/frontEndCourse/unmaintainable.html,译者:coderlmn

如何编写无法维护的代码

让自己稳拿铁饭碗。-- Roedy Green;永远不要(把自己遇到的问题)归因于(他人的)恶意,这恰恰说明了(你自己的)无能。-- 拿破仑;

为了造福大众,在 Java 编程领域创造就业机会,兄弟我在此传授大师们的秘籍。

这些大师写的代码极其难以维护,后继者就是想对它做最简单的修改都需要花上数年时间。

而且,如果你能对照秘籍潜心修炼,你甚至可以给自己弄个铁饭碗,因为除了你之外,没人能维护你写的代码。再而且,如果你能练就秘籍中的全部招式,那么连你自己都无法维护你的代码了!

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

总体原则

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

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

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

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

命名

编写无法维护代码的技巧的重中之重是变量和方法命名的艺术。如何命名是和编译器无关的。

这就让你有巨大的自由度去利用它们迷惑维护代码的程序员。

妙用宝宝起名大全

买本宝宝起名大全,你就永远不缺变量名了。比如 Fred 就是个好名字,而且键盘输入它也省事。

如果你就想找一些容易输入的变量名,可以试试 adsf 或者 aoeu 之类。

单字母变量名

如果你给变量起名为 a,b,c,用简单的文本编辑器就没法搜索它们的引用。

而且,没人能猜到它们的含义。

创造性的拼写错误

如果你必须使用描述性的变量和函数名,那就把它们都拼错。还可以把某些函数和变量名拼错,再把其他的拼对(例如 SetPintleOpening 和 SetPintalClosing) ,我们就能有效地将 grep 或 IDE 搜索技术玩弄于股掌之上。

这招超级管用。还可以混淆不同语言(比如 colour -- 英国英语,和 color -- 美国英语)。

抽象

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

首字母大写的缩写

用首字母大写缩写(比如 GNU 代表 GNU's Not Unix) 使代码简洁难懂。

真正的汉子(无论男女)从来不说明这种缩写的含义,他们生下来就懂。

辞典大轮换

为了打破沉闷的编程气氛,你可以用一本辞典来查找尽量多的同义词。例如 display, show, present。在注释里含糊其辞地暗示这些命名之间有细微的差别,其实根本没有。

不过,如果有两个命名相似的函数真的有重大差别,那倒是一定要确保它们用相同的单词来命名(例如,对于 "写入文件", "在纸上书写" 和 "屏幕显示" 都用 print 来命名)。

在任何情况下都不要屈服于编写明确的项目词汇表这种无理要求。你可以辩解说,这种要求是一种不专业的行为,它违反了结构化设计的信息隐藏原则。

单字母变量名

如果你给变量起名为 a, b, c,用简单的文本编辑器就没法搜索它们的引用。而且,没人能猜到它们的含义。

首字母大写的缩写

用首字母大写缩写(比如 GNU 代表 GNU's Not Unix) 使代码简洁难懂。

真正的汉子 (无论男女) 从来不说明这种缩写的含义,他们生下来就懂。

重用命名

在语言规则允许的地方,尽量把类、构造器、方法、成员变量、参数和局部变量都命名成一样。更高级的技巧是在 {} 块中重用局部变量。

这样做的目的是迫使维护代码的程序员认真检查每个示例的范围。特别是在 Java 代码中,可以把普通方法伪装成构造器。

使用非英语字母

在命名中偷偷使用不易察觉的非英语字母,例如 typedef struct { int i; } ínt;

看上去没啥不对是吧?嘿嘿嘿...这里的第二个 ínt 的 í 实际上是东北欧字母,并不是英语中的 i 。在简单的文本编辑器里,想看出这一点点区别几乎是不可能的。

下划线,一位真正的朋友

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

扩展 ASCII 字符

扩展 ASCII 字符用于变量命名是完全合法的,包括 ß, Ð, 和 ñ 等。

在简单的文本编辑器里,除了拷贝/粘贴,基本上没法输入。

其他语言的命名

使用外语字典作为变量名的来源。例如,可以用德语单词 punkt 代替 point。

除非维护代码的程序员也像你一样熟练掌握了德语. 不然他就只能尽情地在代码中享受异域风情了。

伪装

当一个 bug 需要越长的时间才会暴露,它就越难被发现。-- Roedy Green

编写无法维护代码的另一大秘诀就是伪装的艺术,即隐藏它或者让它看起来像其他东西。

很多招式有赖于这样一个事实:编译器比肉眼或文本编辑器更有分辨能力。下面是一些伪装的最佳招式。

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

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

210c41d5e6c316f5fac59598634bb51b.png

如果不是用绿色标出来,你能注意到这三行代码被注释掉了么?

用连接符隐藏变量

对于下面的定义:

#define local_var xy_z

可以把 "xy_z" 打散到两行里:

#define local_var xy\
_z // local_var OK

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

文档

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

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

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

在注释中撒谎

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

只记录显而易见的东西

往代码里掺进去类似于 /* 给 i 加 1 */ 这样的注释,但是永远不要记录包或者方法的整体设计这样的干货。

记录 How 而不是 Why

只解释一个程序功能的细节,而不是它要完成的任务是什么。

这样的话,如果出现了一个 bug,修复者就搞不清这里的代码应有的功能。

该写的别写

比如你在开发一套航班预定系统,那就要精心设计,让它在增加另一个航空公司的时候至少有 25 处代码需要修改。永远不要在文档里说明要修改的位置。

后来的开发人员要想修改你的代码门都没有,除非他们能把每一行代码都读懂。

计量单位

永远不要在文档中说明任何变量、输入、输出或参数的计量单位,如英尺、米、加仑等。计量单位对数豆子不是太重要,但在工程领域就相当重要了。

同理,永远不要说明任何转换常量的计量单位,或者是它的取值如何获得。要想让代码更乱的话,你还可以在注释里写上错误的计量单位,这是赤裸裸的欺骗,但是非常有效。

如果你想做一个恶贯满盈的人,不妨自己发明一套计量单位,用自己或某个小人物的名字命名这套计量单位,但不要给出定义。

万一有人挑刺儿,你就告诉他们,你这么做是为了把浮点数运算凑成整数运算而进行的转换。

永远不要记录代码中的坑。如果你怀疑某个类里可能有 bug,天知地知你知就好。如果你想到了重构或重写代码的思路,看在老天爷的份上,千万别写出来。

切记电影《小鹿斑比》里那句台词 "如果你不能说好听的话,那就什么也不要说。"。万一这段代码的原作者看到你的注释怎么办?万一老板看到了怎么办?万一客户看到了怎么办?搞不好最后你自己被解雇了。

一句”这里需要修改“的匿名注释就好多了,尤其是当看不清这句注释指的是哪里需要修改的情况下。切记难得糊涂四个字,这样大家都不会感觉受到了批评。

说明变量

永远不要对变量声明加注释。有关变量使用的方式、边界值、合法值、小数点后的位数、计量单位、显示格式、数据录入规则等等,后继者完全可以自己从程序代码中去理解和整理嘛。

如果老板强迫你写注释,就把方法体代码混进去,但绝对不要对变量声明写注释,即使是临时变量!

在注释里挑拨离间

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

2fc5fc86354f17e61debf38a72ffbb57.png

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

程序设计

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

编写可维护代码的关键因素是只在一个地方表述应用里的一个事实。

如果你的想法变了,你也只在一个地方修改,这样就能保证整个程序正常工作。所以,编写无法维护代码的关键因素就是反复地表述同一个事实,在尽可能多的地方,以尽可能多的方式进行。

令人高兴的是,像 Java 这样的语言让编写这种无法维护代码变得非常容易。

例如,改变一个被引用很多的变量的类型几乎是不可能的,因为所有造型和转换功能都会出错,而且关联的临时变量的类型也不合适了。

而且,如果变量值要在屏幕上显示,那么所有相关的显示和数据录入代码都必须一一找到并手工进行修改。类似的还有很多,比如由 C 和 Java 组成的 Algol 语言系列,Abundance 甚至 Smalltalk 对于数组等结构的处理,都是大有可为的。

永远不做校验

永远不要对输入数据做任何的正确性或差异性检查。

这样能表现你对公司设备的绝对信任,以及你是一位信任所有项目伙伴和系统管理员的团队合作者。

总是返回合理的值,即使数据输入有问题或者错误。

有礼貌,无断言

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

避免封装

为了提高效率,不要使用封装。

方法的调用者需要所有能得到的外部信息,以便了解方法的内部是如何工作的。

复制粘贴修改

以效率的名义,使用 复制+粘贴+修改。

这样比写成小型可复用模块效率高得多。在用代码行数衡量你的进度的小作坊里,这招尤其管用。

使用静态数组

如果一个库里的模块需要一个数组来存放图片,就定义一个静态数组。

没人会有比 512 X 512 更大的图片,所以固定大小的数组就可以了。为了最佳精度,就把它定义成 double 类型的数组。

傻瓜接口

编写一个名为 "WrittenByMe" 之类的空接口,然后让你的所有类都实现它。然后给所有你用到的 Java 内置类编写包装类。

这里的思想是确保你程序里的每个对象都实现这个接口。最后,编写所有的方法,让它们的参数和返回类型都是这个 WrittenByMe。这样就几乎不可能搞清楚某个方法的功能是什么,并且所有类型都需要好玩的造型方法。

更出格的玩法是,让每个团队成员编写它们自己的接口(例如 WrittenByJoe),程序员用到的任何类都要实现他自己的接口。这样你就可以在大量无意义接口中随便找一个来引用对象了。

好事成堆 TM

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

1013e4d9663ddc2525d2761a4d63c254.png

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

编码迷局

追求极致

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

b19d7e2e0249b195178e4492462b3b97.png
一致性的小淘气

当你需要一个字符常量的时候,可以用多种不同格式:' ', 32, 0x20, 040。

在 C 或 Java 里 10 和 010 是不同的数(0 开头的表示 16 进制),你也可以充分利用这个特性。

嵌套 Switch

Switch 里边还有 Switch,换着花样来,多套几个。这种嵌套方式是人类大脑难以破解的。

使用八进制数

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

8350a933d878895ba83513dad8c5b8a0.png
狂热奔放地使用线程/协程

如题。

像 Go 可以疯狂起 goroutine,不加 recover。各种嵌套!

总结

本文的篇幅已经比较长了,我仅截取了一部分给大家分享。整体来讲,从 “防御性编程” 的角度来讲,原作者分享的技巧还是非常 “实用“ 的。

如果能够高效的准确落地在工作编码中,那有些同事肯定会给你 ”点赞“。不过本文主要是结合现在 ”开猿节流,降本增笑“ 的主题,但是看着一乐就好。

希望对大家有所帮助:)

推荐阅读

关注和加煎鱼微信,

一手消息和知识,拉你进技术交流群👇

2778a74fd065c491274e69d3035bffef.jpeg

4a74a499289ef7c505ffd6a7139fc839.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值