当我开始编程时,所拥有的处理器执行速度很慢,内存空间也非常有限 —— 有时仅以KB衡量。因此,必须合理考虑内存的使用和优化。
在大学里,我们知道了优化的两个极端情况:
如今,没有人会太在意内存的使用(除了demoseners,嵌入式系统工程师,部分手游开发者),不仅是对于RAM空间,硬盘空间也是。 试想一下仅安装看门狗就耗费硬盘近25Gb 空间。 此外,我在谷歌浏览器选项卡中写这篇文章时,占用了130Mb的RAM空间。
实际上需要优化的对象有很多:
优化可读性——让代码更容易阅读、跟踪和理解。
你应该明白,你在优化时难以兼顾各个方面。 例如,当致力于性能优化时,你很可能让应用程序内存消耗增加,同时代码可读性也变差。
开发者大量工作时间并不是在编写代码,而是阅读代码,调试代码,查阅他人提交的开发文档,学习新的库等。
当阅读代码时,开发者实际上是充当代码解释器的角色(虽比不上计算机)—— 在他们的头脑中执行代码,并试图记住当前执行状态。 这就是程序员在阅读代码过程中被打搅脾气暴躁的原因。
时间== 金钱
你应该意识到一件很最重要的事,是你和你的同事浪费了大量时间。 即使是一个努力工作的开发者,在做下面的事时仍然浪费了大量时间:
上述情况是假设遇到问题的开发者经验丰富并且熟知高效算法和简洁代码如何书写,否则要列出的情况要更多。
唐纳德·克努特说过一句名言。 我敢打赌你听过很多次。
“在编程中,过早优化是万恶之源。 ” —— D.Knuth,1974
我遇到很多知道这句话的人,但真正理解这句话的却很少。 最常见的错误理解像这样:
我想这是由于对过早优化这个词没有明确界定的原因。 这就是这些人一点也不认为他们那么做属于过早优化的原因。 那么,我们该如何界定这个词呢?
过早优化——在工作系统中分析和运行测试前的任何优化尝试。
除可读性之外任何修改都属于过早优化。 所以,与其说一个人不应该做什么,不如说应该做什么。 那么,这句引言可以这样理解:
优先提高可读性。
好吧,我们一致认为,我们应该让代码更易于阅读,这样可以节约时间和金钱,对吧?但这究竟意味着什么?
有迹象表明,下面两个基本方面极大地降低了开发者阅读代码的速度:
遗憾的是,人们并不能像软件解释器那样,可以不必理会将两个数相加并调用一个函数这部分代码的功能(机械式的编译)。
为了查找代码异常的原因,程序员必须理解源程序中编写的代码实现了何种功能,编写的初衷是为了实现何种功能。
下面情况是对于经验丰富的开发者而言,这些开发者熟悉代码开发使用的语言和程序中使用的算法(即他们有足够的知识来理解这段代码)。
我将跳过前两条,因为无论如何你不应该阅读不良代码。 如果你所在的公司有人编写了不良代码,你应该纠正它们或者将其废弃。 当然,你必须为你的整个代码库执行严格的编程规范。
亦或所谓的行数优化。 嵌套函数调用和条件运算符的长行代码难以剖析。 当然,你可能会说这种观点是片面的。 但这些人觉得源程序代码越短越好,不必考虑可读性。
最初,代码的可读性很好,工作也很稳定,但有些人决定在某些方面对其进行优化。 经过认真剖析,这可能是一个很好的优化,但此时的代码看上去像是数组、位运算和幻数的结合体。 没有人知道代码在做什么,甚至代码应该做什么,因为完成优化的人没有提交任何说明。
也许你听说过优秀的代码不需要说明文档。 但是经过优化的代码(特别是优化效果很理想的情况)必须要有说明文档。
在你的代码库中,可能大部分的优化只是像这样的未备注行:
1
|
if
(val != val) { ... }
|
作为软件开发者,我们掌握越来越多的学术技巧并将其运用到实际代码开发中。 毕竟,我们是计算机科学家,而不只是码农!
有些语言甚至鼓励开发者使用前沿技术,使代码更具表现力和学术性。 当你用代码建立了一个非常健壮的系统,特别当你用数学方法证明了一个高深定理,而99.997%受过教育的人对这种方法都不理解,你就会有这种成就感。
即使代码被良好地封装成模块/类/函数并且这些模块包含完全可读的命令式代码,但其他人想要读懂这段代码,他们必须掌握整个代码的框架以及所有使用的相关技术和模式。
再一次强调,记住“其他人”可能就是一周后的你。
极可能这是我在工作中仅认识两个使用Scala语言人的原因。就我个人而言,非常喜欢Scala语言。 对我来说,它就是一个学术操场,我可以在那里建造玻璃城堡。 一旦你越了解它,它的越多特性也就能为你所用,你也就越明白它本质上只是一门编程语言(请不要在这里引用我!)。
虽不如Perl语言,但即使最漂亮的代码库也需要修改和更新。 现在,你需要寻找一个能够理解这些优美代码的人……
简洁高明的代码难以阅读似乎是有争议的。
“软件调试要比编写代码困难一倍,如果你发挥了最大才智编写代码,那么你的智商便不足以调试这个代码。 ” —— Brian Kernighan
阅读代码时,通常需要频繁的从一个函数或类跳转到另一个函数或类。 掌握你使用的集成开发环境(IDE),可以节约很多阅读时间。 通过使用集成开发环境(例如Visual Studio)的“跳转至声明”,“查找使用”,“导航至”,“检查”等特性,你可以将整个代码看作是一幅连通图。
在Notepad中编写代码是不错的选择,但是如果你想有效的阅读代码,必须掌握一个集成开发环境。
那么,究竟什么是连通图呢?
连通图是在拓扑空间中连接的图,即图中任意两点之间都有一条通路。(来源)
换句话来说,在“连通”代码中,你可以方便的从一个方法中跟踪到另一个方法中,并在你头脑中建立这段代码的功能框架。
如果代码中某一部分链接被破坏(在这种情况下,集成开发环境不能帮助你实现函数间的跳转),通常你必须花一些时间自己查找链接。代码中被破坏的链部分越多,越难以跟踪,代码也就越难以阅读。
那么,为什么代码图会被断开?原因是多方面的,下面将列出一些常见情况:
一些框架就喜欢这样做,他们将”回调”作为字符串传递并在需要时使用反射。 此时你需要使用CMD+F查找。
最可恶的是动态语言中的动态字符串…… 对这个问题,向JavaScript或AS3致敬!
例如,你的代码一半使用C#编写,另一半是在可视化节点编辑器生成。 在这两者之间跳转非常不易。
依赖注入框架和其他XML配置工具也是。虽然没有明确说明,但编写XML配置文件也属于编程。 这就是所谓的的声明式编程(更不用说那些构建基于XML命令式语言的疯狂的人)。
20个链接跳转到这个包含1000行代码的函数?。。哎哟。 你不需要包含这种节点的图。
通过跳转至声明,你可到达一个接口或者一个抽象类,必须弄清楚有哪些实现。 依赖注入,抽象工厂和其他所有反对依赖的方法使得这一切变得更糟。 代码图中节点间的联系过于抽象。
这样说来,我讨厌依赖注入(DI)和扩展标识语言(XML)。但DI是一个很棒的工具,它可以让你避免书写面条式代码并让程序的架构更加模块化,更具可测试性。但像其他好的事物一样,过度依赖必然产生负面效果。
我曾在审查一个应用程序时感到完全气馁,因为我意识到自己弄不明白程序从何处开始。。。例如它的入口点在哪。 这一切都是在程序开始时从XML配置工具自动生成。
但我确实讨厌XML配置工具。
***
所以,到这里你应该已经学会:
强迫自己编写简单的代码,避免在早期阶段优化确实有一定难度,这需要花费时间。
在截止期前2小时已连续工作48小时,如果你在半睡半醒的状态下能够阅读你所使用的代码,你应该对过去的自己说声“谢谢”。