在编写数学证明时,一个目标是继续压缩证明。证明变得更加优雅,但不一定更具可读性。压缩转化为更好的理解,因为您清除了不必要的字符和冗长。
我经常听到开发人员说你应该让你的代码占用空间尽可能小。这会很快产生不可读的代码。在数学中,这不是一个问题,因为该练习纯粹是学术性的。然而,在时间就是金钱的生产代码中,让人们试图弄清楚一些非常简洁的代码在做什么似乎没有多大意义。对于更冗长的代码,您可以获得可读性和节省。
您在什么时候停止压缩软件代码?
我试图达到某种程度的冗长,使我的程序语句读起来像任何程序员都能理解的句子。这确实意味着对我的代码进行大量重构,使其成为故事的一小部分,因此每个动作都将在一个单独的方法中进行描述(更进一步的层次可能是另一个类)。
这意味着我不会仅仅因为它可以用更少的方式表达就减少我的字符数。这就是代码高尔夫比赛的目的。
我的规则是说出你的意思。我看到人们出错的一种常见方式是“降低力量”。基本上,他们用似乎跳过步骤的东西代替了他们正在考虑的概念。不幸的是,他们在代码中遗漏了概念,使其更难阅读。
例如,改变
for (int i = 0; i < n; i++)
foo[i] = ...
到
int * p = foo, q = foo+n;
while ( *p++ = ... < q );
是一个强度降低的例子,它似乎可以节省步骤,但它忽略了 foo 是一个数组的事实,使其更难阅读。
另一个常见的方法是使用 bool 而不是枚举。
enum {
MouseDown,
MouseUp
};
有了这个
bool IsMouseDown;
忽略了这是一个状态机的事实,使代码更难维护。
所以我的经验法则是,在你的实现中,不要深入到比你试图表达的概念更低的层次。
您可以通过查看冗余并消除它或通过聪明来使代码更小。做前者而不是后者。
这是 Steve McConnell 的一篇好文章 - 最佳实践http://www.stevemcconnell.com/ieeesoftware/bp06.htm
我认为简短/简洁是编写良好的代码的两个结果。使代码好有很多方面,写得好的代码有很多结果,意识到两者是不同的。你不打算为一个小的足迹,你计划一个简洁的功能,并且做得非常好 - 这应该导致一个小的足迹(但可能不会)。以下是我在编写代码时会关注的内容的简短列表:
找到平衡的一种方法是寻求可读性而不是简洁性。程序员不断地在视觉上扫描代码以查看正在执行的操作,因此代码应该尽可能流畅。
如果程序员在扫描代码时碰到了难以理解的部分,或者费了一番功夫去视觉解析和理解,那是一件坏事。使用常见的易于理解的结构很重要,除非必要,否则远离模糊和不经常使用的结构。
人类不是编译器。编译器可以吃掉这些东西并继续前进。晦涩的代码不会像清晰理解的代码那样迅速地被人类在精神上消耗掉。
有时很难用复杂的算法生成可读的代码,但在大多数情况下,我们应该寻找的是人类可读性,而不是聪明。我也不认为代码的长度真的是清晰的衡量标准,因为有时更冗长的方法比简洁的方法更具可读性,有时简洁的方法比长的方法更具可读性。
此外,注释应该只是补充,而不应该描述你的代码,你的代码应该描述自己。如果你必须评论一行,因为它不明显做了什么,那很糟糕。大多数有经验的程序员阅读英文解释比阅读代码本身需要更长的时间。我认为这本书 Code Complete 敲定了这一点。
就对象名称而言,随着新编程语言的引入,对此的思考已经发生了演变。
如果您采用“花括号”语言,从 C 开始,简洁被认为是智慧的灵魂。因此,例如,您将有一个变量来保存名为“lv”的贷款值。这个想法是你输入了很多代码,所以尽量减少击键。
然后出现了微软认可的“匈牙利符号”,其中变量名的第一个字母表示其基础类型。有人可能会使用“fLV”或类似的词来表示贷款价值是由一个浮点变量表示的。
使用 Java,然后是 C#,范式已经变得清晰。贷款价值变量的好名字是“loanValue”。我相信部分原因是大多数现代编辑器中的命令完成功能。由于不再需要输入整个名称,因此您可以使用尽可能多的字符来进行描述。
这是一个很好的趋势。代码需要易于理解。评论通常是事后才添加的,如果有的话。它们也不会随着代码的更新而更新,因此它们已经过时了。描述性的、精心挑选的变量名是让其他人知道你在编码什么的第一、最好和最简单的方法。
我有一位计算机科学教授说:“作为工程师,我们不断地创造出以前从未存在过的事物类型。我们给它们起的名字会一直存在,所以我们应该小心地给事物起有意义的名字。”
需要在简短的源代码和性能之间取得平衡。如果它是好的源并且运行速度最快,那么很好,但是为了好的源,它像狗一样运行,那就不好了。
努力重构,直到代码本身读得很好。你会在这个过程中发现你自己的错误,代码会更容易被“下一个人”理解,并且你不会因为在评论中维护(后来忘记更改)你已经表达过的内容而感到负担代码。
当失败时......当然,给我留言。
并且不要在评论中告诉我“什么”(这就是代码的用途),告诉我“为什么”。
相对于长/漫无边际?当然!
但它到了它如此简短、如此简洁以至于难以理解的地步,那么你就走得太远了。
是的。总是。
DRY:不要重复自己。这将为您提供既简洁又安全的代码。多次编写相同的代码是使其难以维护的好方法。
现在这并不意味着您应该为任何看起来很相似的代码块创建一个函数。
例如,一个非常常见的错误(恐怖?)是分解代码做几乎相同的事情,并通过向函数 API 添加标志来处理出现之间的差异。起初这可能看起来无害,但生成的代码流难以理解且容易出错,甚至更难重构。
如果您遵循常见的重构规则(查看代码异味),您的代码将变得越来越简洁,因为许多代码异味都是关于检测冗余的。
另一方面,如果你试图使代码尽可能短,而不遵循任何有意义的指导方针,那么在某些时候你将不得不停下来,因为你再也看不到如何减少代码了。
试想一下,如果第一步是删除所有无用的空格......在大多数编程语言中的该步骤代码将变得如此难以阅读之后,您将没有太多机会找到任何其他可能的增强功能。
上面的示例非常讽刺,但与您在不遵循任何明智准则的情况下尝试优化尺寸时所得到的结果相差无几。
没有确切的界限可以区分 glib 的代码和华丽的代码。用你最好的判断。让其他人查看您的代码,看看他们理解它的难易程度。但请记住,正确性是第一目标。
对小代码足迹的需求是从汇编语言和第一个稍微高级的语言时代的倒退......那里有真正迫切需要的小代码足迹。但是,这些天来,它并不是必需品。
也就是说,我讨厌冗长的代码。在我工作的地方,我们编写的代码尽可能像自然语言一样阅读,没有任何额外的语法或单词。我们不会缩写任何东西,除非它是一个非常常见的缩写。
Company.get_by_name("ABC")
makeHeaderTable()
就像我们去的一样简洁。
总的来说,我使事情变得明显且易于使用。如果简洁/简短在这方面对我有用,那就更好了。通常简短的答案是最清楚的,所以简短是显而易见的副产品。
我有几点决定何时停止优化:
值得花时间进行优化。如果有人花费数周时间却没有找到任何东西,是否可以更好地利用这些资源?
优化优先级的顺序是什么。当涉及到代码时,人们可能会关心几个不同的因素:执行时间、执行空间(运行和编译的代码)、可扩展性、稳定性、实现了多少功能等。其中一部分是交易脱离时间和空间,但它也可能是一些代码的去向,例如中间件可以执行即席 SQL 命令还是应该通过存储过程路由以提高性能?
我认为主要的一点是,大多数好的解决方案都会有一个适度的。
代码优化与编码风格几乎没有关系。文件包含的 x 空格或新行少于开头的事实并不能使它更好或更快,至少在执行阶段 - 您使用编译器通常忽略的白色字符格式化代码。它甚至使代码变得更糟,因为它对其他程序员和你自己来说变得不可读。
更重要的是代码的逻辑结构要简洁干净,例如测试条件、控制流、假设、错误处理或整体编程接口。当然,我也会在此处包含聪明而有用的评论 + 文档。
简洁的代码和性能之间不一定存在相关性。这是一个神话。在 C/C++ 等成熟语言中,编译器能够非常有效地优化代码。在这些语言中,有必要假设越简洁的代码就是性能越好的代码。像 Ruby 等较新的、性能优化较少的语言缺乏 C/C++ 编译器的编译器优化功能,但仍然没有理由相信简洁的代码性能更好。现实情况是,在代码投入生产并进行分析之前,我们永远不知道代码在生产中的性能如何。如果从代码中足够多的位置调用简单、无害的函数,可能会成为巨大的性能瓶颈。在高并发系统中,最大的瓶颈通常是由糟糕的并发算法或过度锁定引起的。
底线是:一旦性能分析确定它是瓶颈,总是可以重构性能不佳的代码。只有易于理解的代码才能有效地重构。写得“简洁”或“聪明”的代码通常更难重构和维护。
编写代码以提高人类可读性,然后在必要时重构性能。
我的两分钱...
代码应该简短、具体、集中。你总是可以在评论中用很多词来解释你的想法。
只要您对其进行注释,您就可以使您的代码尽可能短或紧凑。这样你的代码可以优化但仍然有意义。如果仍然不清楚,我倾向于使用描述性变量和方法以及稀疏注释留在中间的某个地方。