纯粹的代码读起来像写得很好的散文。
格雷迪·布奇
重新编码

什么是转载? 这是一条加密的消息。 笔迹的作者采用普通的人类文字,并使用绘图,数字和字母对其进行编码。 我们研究这种加密并尝试读取源文本。
重写有两种形式。 一方面,重传是原始未加密的文本,另一方面,是密文图。 文本是重述的“内容”,即含义,消息。 图片是“如何”的:通过
何种方式对消息进行精确的加密。 猜中谜题,我们将“如何”转换为“什么”。
绘画是谜语的语言,是表达手段的武器库。 rebusnik确实是在这些图纸的帮助下与我们交流的,传达了一些信息。 不允许他使用普通的人类语言。
这是谜题的读法:

该代码就像是重复
程序代码与rebus有一些共同点:它也有自己的“什么”和“如何”。 而且有时还必须解密。
代码的“含义”是其目的,含义,效果以及我们期望的最终结果。
他到底在
做什么 。
代码的“方式”-通过什么具体的方式,通过什么具体的赋值,乘法,比较来完成其“什么”; 算法的实现,指令给处理器。 它是一种允许的代码语言,它是表达手段的库。
马丁·福勒(Martin Fowler)以此方式谈论它(
“函数长度” ,
原文 ):
那些年里,Smalltalk在黑白机器上工作。 如果需要突出显示文本或图形,则必须反转视频。 Smalltalk中负责计划的类包含“突出显示”方法,在其实现中只有一行-调用“反向”方法。 该方法的名称比实现的名称更长,但是没关系,因为此代码的意图和实现之间的距离很大。
这里重点是“什么”。 用马丁的术语来说,
意图是 :“突出显示图像的一部分。” 名称表示此功能的作用。 反向是如何
实现的 。
如何精确执行突出显示(使用图像反转)。 那就是“什么”和“如何”之间的区别。
尽管该方法的名称比其实现的名称更长,但是由于非常简单的原因,这种方法的存在还是有意义的。 当我们在代码中看到一个反向调用时,我们必须了解或记住图像反转用于使该图像更加可见。 当我们看到高亮显示时,我们读到:“使该片段更加可见。” 在第一种情况下,我们花了些心血来理解分配给代码的任务,在第二种情况下-否。 在第一种情况下,我们看到眼前的重传需要解密,在第二种情况下,这是一个易于理解的语言故事。
程序员在编写程序时就像是重用。 程序员使用可用的编程语言工具(比人类语言原始得多)对算法的人类描述进行加密。 用“如何”加密“什么”。 后来,他或他的同事读取了代码,破译了
这些代码,
重新演算法的最初描述。
如果在阅读代码的过程中我们无法立即了解每个片段的执行将导致什么结果,即代码的目的和意义是什么,那么该代码就是重用,需要用一种清晰的语言重写。代码中的难题的问题在于,它们
总是需要精神上的努力。 即使我们在脑海中没有进行全套解密操作,只是愚蠢地记住一些重传的含义,它仍然会产生负载:首先,记住其含义,其次,在录制转换时以此值进行重新计算。
蒂姆·奥廷格(Tim Ottinger)在“清理代码”一书中谈到的,是在思维上进行的非常精神上的转变,在阅读代码时解密重用。 的确,他在为变量分配可理解的名称的上下文中讨论了它们,但是问题完全相同。 提姆的话:
通常,程序员非常聪明。 聪明的人有时喜欢表现出智力的力量,展示出他们在心理上变魔术的能力。 最后,如果您还记得变量r包含一个带有远程主机的URL以及一个转换为小写形式的方案,那么这显然表示您的想法。
聪明的程序员和专业的程序员之间的区别之一是专业人士理解:清晰度至关重要。 专业人士善用自己的力量,编写其他人可以理解的代码。
如果有很多这样的重载,那么即使每个重载的负载很小,也会变成问题。 您可能遇到了精疲力尽的代码。
知道:代码中的重复是造成您疲劳的原因。 在编写代码的过程中,重用甚至直接加剧了自己作者的疲劳。 毕竟,在编写代码的同时,程序员还不断地重新读取所写的内容。 尽管作者并没有解密自己的难题,只是记住,它们仍然会产生负担。 陷阱是
作者根本看不到自己代码中的困惑 ! 试想一下,如果早晨开始摆脱代码中的困惑,那么晚上可以节省多少精力!
因此,为了减少编写和阅读代码的疲劳,您需要避免困惑。 但是怎么做呢?
代码的语言。 标识符强度
我同意Grady Butch的说法,即干净的代码读起来就像是散文。 这是必要的条件,尽管还不够充分。 我们大多数人都会直观地了解要解决的问题,但是我想至少得到一些定义:这是什么-好的散文。
我问过我的作家们:好散文与坏散文有何不同? 每个人的回答都不同,但是以某种方式强调了语言的重要性:它必须丰富,必须在读者的思想和灵魂上创造清晰的图像。 当读者轻松拥有作者想要为其绘制的清晰图片时,我们正在处理好的散文。
该代码告诉处理器它应该做什么。 一个好的代码同时可以告诉程序员-而且,这是非常真实的! -他在这里做什么。 也就是说,它提出的算法尽可能接近作者本人将以自然语言完成的算法。 我们的代码必须做得很好,否则可能会
带着电锯或shot弹枪肆无忌ia地来到我们家。 该代码不应是重复代码。
该代码有哪些工具才能不被滥用?
如果代码本身使用人类语言说话,那么代表代码的故事将很容易被人理解。 这只能借助标识符来实现:函数,类,变量和常量的名称-因为
只有在标识符中,我们才能使用所需的人类语言的任何单词 。
当然,编程语言的关键字也是人类的单词,但是它们的词汇太糟糕了。 像食人魔埃洛奇卡(Ellochka the Ogre)的语言-您不能在上面写好散文。
因此,至关重要的是,程序代码应包含尽可能多的正确选择的标识符。 这样它们的整体就构成了写得很好的散文。
选择合适的变量和方法的名称,看看读取代码行是多么容易:
pageData.hasAttribute("Test") dom_tree.to_html() emails_str.split(',')
看看这些简短的短语,就很容易理解他们在说什么。 我们知道会得到什么结果,因为标识符会告诉我们有关结果。 现在,想象在每个这样的调用的地方是它的实现-这样的“加密”代码的读取速度会降低多少?
许多最简单的重构技术:命名常量,选择方法,用方法调用替换变量,解释变量,拆分临时变量等,都是关于
如何使代码讲人类语言,换句话说,如何避免困惑 。
中继方式
当我阅读《 Pure Code》时,我经常被这样的想法所吸引:“该死!”。
罗伯特·马丁(Robert Martin)从他40多年的经验中为我们提供了有关如何使代码更好的技巧。 例如:
第一条规则:功能应该紧凑。 第二条规则:功能应该更加紧凑。
然后他承认,他无法在科学上证实其主张。 老实说,不科学,他也做得不好。 对功能紧凑性的要求已经开始看起来像教条,这就是为什么
关于功能长度问题的
争论已经有数十年没有消退的原因了。
Bob还提供编写每个函数的功能,以便仅执行一个操作。 而且,这是什么操作-也不是很清楚。 我们必须寻求帮助,即单一抽象级别的原则,这会使情况进一步混乱。 这一切太迷雾了。
马丁·福勒(Martin Fowler)更务实。
在我看来,关于意图和实现分离的争论更具意义。 如果看一段代码,您需要努力了解它在做什么,那么您需要将其放入函数中,并根据此“内容”为其命名。 然后,下一次该功能的用途将立即变得很明显,并且在大多数情况下,您无需关心该功能如何执行其工作。
原来的然而,对我而言最有意义的论点是意图与执行之间的分离。 如果您必须花精力去看一段代码来弄清楚它在做什么,那么您应该将其提取到一个函数中,并以“什么”命名。 这样,当您再次阅读该功能时,该功能的目的便会自动出现在您身上,并且在大多数情况下,您无需关心该功能如何实现其目的-这就是该功能的主体。
已经更好了。 您现在看到马丁在这段话中想说什么吗? 他的意思是:让我们消除困惑。 让代码本身告诉我们结果将是什么,以及如何-让它隐藏在函数定义中更远的地方。 让所有难题解开。 没有难题-无需努力。
在任何情况下都不应盲目地应用重构方法。 这是如此明显,但是如何理解何时真正需要重构以及何时不需要重构呢?
重构方法说:如果重构后重构没有消失,则不需要重构 。
如果找不到新功能的名称可以清楚地解释其中发生的事情,则可能是您在做错事情。 尝试在函数中选择稍有不同的代码片段-您可以在该片段中快速找到一个易于理解的简称。用代码解码难题的例子(不是很成功)
因此,我将引用我喜欢的《清洁代码》一书的片段。 在重构之前,我们看到充满困惑的代码。 重构是由该书的作者根据他所提倡的良好代码规则完成的,并且-巧合的是-重构后的代码看起来完全像是对解密进行解密的代码。
重构作者完全应用了人类可读的标识符(类名,方法和变量)来指示代码实际上在做什么。 遗憾的是,并非所有地方都成功实现了这一目标,而且在某些地方出现了新的难题,而不是以前的困惑。
例如,此段落中最常用的include方法
private void include(String pageName, String arg) throws Exception { WikiPage inheritedPage = findInheritedPage(pageName); if (inheritedPage != null) { String pagePathName = getPathNameForPage(inheritedPage); buildIncludeDirective(pagePathName, arg); } }
该名称完全不反映实施中发生的情况。 包括什么,在哪里?
查看此方法的调用:
include("TearDown", "-teardown");
在这里无法说出代码作者将要实现的结果。
Next:buildIncludeDirective有什么作用? 从名称来看,他应该制定某种包含指令,那又如何呢? 带她回来吗 但是没有 他立即将其添加到总体结果中。
这是另一个updatePageContent。 在调用方法后,updatePageContent告诉我们什么结果? 没事 那里的某些页面内容将被没人知道的内容取代。 为什么在这里执行称为方法提取的重构? 他有没有帮助摆脱困境? 它没有帮助,但只会使代码更加混乱。 在这种情况下,最好使用该方法的主体。 建筑施工
pageData.setContent(newPageContent.toString());
比神秘的updatePageContent()清晰得多。
作为一种娱乐活动,我建议读者寻找
重构代码中还有哪些其他不好的地方。
为了证明鲍勃的合理性,我可以说这段代码不再存在于FitNesse的当前版本中。 显然,他也曾经被重构过。
结论
功能的长度对于确定功能的质量而言过于模糊。 “简短功能”不等于“良好功能”。 函数的长度不是一个标准,算了吧。
好的代码应该为程序员的问题提供答案-他为什么在这里(代码),
他在这里
到底在做什么 ,
他正在取得结果。 这些答案只能使用标识符给出。
作为示例代码不应给出的答案的一个例子,我想摘录一本有趣的书。
“我是罗南,邪恶的胜利者,”他慢慢地说。 -这是塔尔 我们要你一些
问问题。 如果撒谎,你就会死。 知道了
“我,叔叔,永远。”他呼吸。 拜托 我会说全部。
“很好,”罗南继续说道。 -名字?
-Ronan,邪恶的胜利者。
-是的,不是我的,白痴!
“啊,是的,然后是塔尔,”兽人道歉地回答。
-不是我的! 喃喃自语的塔尔。 -你的名字,俱乐部! 名!
“这个名字是我用来区别别人的名字,”兽人喃喃地说。
-好吧,在这里给这个名字! 大喊的塔尔。
Orka忽然忽然醒了。
-啊! imple!
“那么粉刺,你在这里做什么?”
真实的回答是:“我把它放在裤子里。”
罗南厌恶地皱了皱鼻子。
“不,我问你那帮兽人在这里做什么!”
丘疹的眼睛迅速转过身,环顾四周。
他喃喃地说:“这里的大多数人都没有头。”
Tarle碰到Ronan的肩膀。
“让我尝试,”他自信地说,转向那只受惊的兽人。 -告诉我,
丘疹,“他继续说道,”你为什么在这里?
-叔叔,别问。 对我来说,生存哲学只是一片黑暗。
“听着,你这龙打,”他闷闷地咆哮。 -您那帮兽人有一个特殊的
来这里的理由。 在森林里是什么?
-有很多树。
罗南的眼睛鼓了起来,塔尔转过身去。 感觉自己没有给出预期答案的丘疹开始喃喃自语。
-如果您想知道原因而不是森林,那都是因为酒吧里的那个人
付钱给我们杀了你
詹姆斯·比比(James Bibby),蛮族罗南(Ronan)