当前位置: 首页 > 科技观察

好代码和坏代码

时间:2023-03-14 22:09:57 科技观察

要写出好代码,首先要提升自己的品味。很多软件工程师写不好代码,在review别人的代码时也看不出问题,因为他们缺乏好的编码标准意识。仍然有太多的软件工程师认为代码只要正确执行就可以了。这是一个很低的评价标准,忽略了很多重要的方面。好代码的特点好代码具有以下特点。1、健壮(SolidandRobust)代码不仅要正确执行,还要考虑对各种错误情况的处理,比如各种系统调用和函数调用的异常情况,系统相关组件的异常和错误。对于很多生产级的程序来说,异常和错误处理的逻辑占了很大的比重。2.高效(Fast)程序的运行应该使用尽可能少的资源。资源不仅包括CPU,还包括存储、I/O等。要设计一个高效的程序,会应用数据结构和算法的知识,还要考虑程序运行时的各种约束。3、可维护性、简单性:代码的逻辑要尽可能简洁易懂,代码要有良好的可维护性。同一个目标,如果能用简单明了的方法去实现,就不要用复杂晦涩的方法。“从大道至简”,复杂的问题能否用简单的方式实现,是编程水平的体现。4.小从某种意义上说,代码的复杂度和维护成本与代码的大小直接相关。在实现相同的功能时,尽量把代码写得短一些。简洁胜过简洁。这里需要注意的是,有些人为了把代码写的简短一些,使用一些晦涩难懂的描述方式,降低了代码的可读性。这种做法不可取。5.可测试(Testable)代码的正确性必须通过测试来保证,尤其是在敏捷场景下,更需要依赖可以自动回归执行的测试用例。在代码的设计中,需要考虑如何使代码可测试,易于测试。更好的做法是使用TDD(Test-DrivenDevelopment,测试驱动开发)方法,这样在编写测试用例时,你会很快发现代码的可测试性问题。6.Shared(Re-Usable)大量的程序实际上使用了相似的框架或逻辑。由于开源代码的流行,很多功能不需要重复开发,仅供参考和使用。在一个组织内,应该鼓励代码共享和重用,这可以有效降低代码开发成本,提高代码质量。实现代码共享不仅需要意识的提升,还需要相关的能力(比如编写独立的、高质量的代码库)和相关的基础设施支持(比如代码搜索和代码引用机制)。7.便携(Portable)有些程序需要在多种操作系统下运行。在这种情况下,代码的可移植性就成为了必备的能力。为了使代码可移植,需要对其运行的各种操作系统的底层有充分的理解和统一的抽象。通常,适配层用于屏蔽操作系统的底层差异。一些编程语言还提供了对多个操作系统的可移植性。例如,许多用Python、Java和Go编写的程序可以跨平台运行。8.Observable/Monitorable面对海量的在线服务程序,需要具备仔细、持续监控程序运行状态的能力。这就需要在程序设计时提供相关的机制,包括程序状态的收集、存储和对外输出。9.运维运维已经成为软件研发活动的重要组成部分,运维关注成本、效率和稳定性三个方面。程序的可操作性与程序的设计和编写密切相关。如果在程序设计阶段不考虑可操作性,那么程序运行的运维目标就很难实现。10、可扩展性(ScalableandExtensible)可扩展性包括两个方面:“容量可扩展”(Scalable)和“功能可扩展”(Extensible)。在互联网公司的系统设计中,“容量可扩展”是重要的设计目标之一。系统应该尽量通过增加资源来支持容量的线性增长。快速响应需求变化是互联网企业面临的另一重要挑战。可以考虑使用插件化的编程方式来容纳未来可能出现的新功能,也可以考虑使用ProtocolBuffer这样的工具来支持在协议中增加新的字段。如果你想记住上面的十个标准,可能很难。我们可以将它们概括为四个方面,见表1。表1.一级代码特征的总结和分类。坏代码的例子关于好代码,上面介绍了一些特点。本节还给出了几个错误代码(BadCode)的例子。关于烂代码,本书不做系统的总结,只希望通过下面的例子,让读者对烂代码有一个直观的感受。1.错误的函数名(BadFunctionName)如do(),这样的函数名信息量不大;再比如myFunc(),这样的函数名太个人化了,信息量不够。2.坏变量名(BadVariableName)如a,b,c,i,j,k,temp,这样的变量名经常出现在很多课本中,很多人在学校使用的时候写代码的时候也经常这样。这样的名称有时可以作为局部变量接受;然而,这样的名称对于范围稍大的变量来说是非常不可取的。3.NoComments(无评论)很少有软件工程师有写评论的习惯。许多软件工程师认为写注释是浪费时间,是“额外”的工作。但是没有注释的代码,阅读成本会比较高。4.TheFunctionhasNoSinglePurpose(函数没有单一目的),如LoadFromFileAndCalculate()。这个例子是我杜撰的,现实中有很多这样的函数。许多函数在第一次编写时,很难清楚地表达它们的用途;有些功能随着功能的扩展变得越来越复杂,其用途也逐渐不明确。这方面的问题可能很多人都没有完全意识到——非单一用途的功能很难维护,也很难重用。5、不良布局(BadLayout)很多人认为程序可以正常执行,所以一些软件工程师不注意代码的布局,认为它只是一个“表格”。如果没有一个类型良好的程序,将会导致阅读效率的严重问题。下面是一个极端的例子:对于C语言,“;”可以作为语句分隔符,而“缩进”和“换行”对编译器来说是无用的,所以完全可以把一个C语言程序“压缩”在一行。这样的程序可以运行,但对人类来说可读性很差。这样的节目,绝对是我们非常不愿意看到的。6.不可测试(NoneTestable)程序的正确性要靠测试来保证(虽然测试并不能保证程序完全没有错误)。对于不能或没有很好地编写测试用例的程序,很难有质量保证。好的代码从何而来?上一节解释了好代码的特征。本节分析好的代码是如何产生的。好代码不仅仅是编码好代码从何而来?对于这个问题,很多读者肯定会说:“好的代码一定是写出来的”。我做了很多研究,发现很多软件工程师每天看的书确实和“写代码”息息相关。不过,这里要告诉读者的是,代码并不是“写出来的”而已。多年前,我阅读的软件工程教科书告诉我,编码通常只占项目时间的10%。我曾经说过一句有趣的话:“如果一个从业者告诉你,他大部分时间都在写代码,那么他很可能不是高级软件工程师。”那么,软件工程师的时间都去哪儿了?羊毛布?软件工程师应该在哪里度过他的时间?好的代码是多个工作环节的综合结果。(1)编码前,要做好需求分析和系统设计。这两项任务往往被大量软件工程师忽视或低估。(2)编码的时候需要写代码,写单元测试。对于“写代码”,读者理解;但对于“写单元测试”,有些软件工程师不以为然,甚至有人错误地认为单元测试是测试工程师写的。(3)编码完成后,做集成测试、上线、持续运营/迭代改进。这几件事情是很费功夫的,比如上线,不仅要做程序部署,还要考虑程序如何监控。有时需要几天的时间来设计和实施监控解决方案才能让项目上线。因此,一个好的系统或产品,就是以上几个环节不断循环执行的结果。需求分析与系统设计一、几种常见的错误相对于编码工作,需求分析与系统设计是两个经常被忽视的环节。在实际工作中,我们经常会看到以下现象。(1)很多人错误地认为写代码才是最重要的事情。很多软件工程师如果一天不写几行代码,就会认为工作没有进展;很多管理者还会以代码的输出作为衡量工作成果的主要标准,督促软件工程师尽快开始编写代码。(2)太多的从业者在没有弄清楚项目目标之前就开始编码。在许多情况下,项目目标是通过不准确的口头交流确定的。例如:“需要做什么?”“按照×××网站做一个就行了。”(3)有太多的从业者在基本写完代码后才发现设计思路有问题。他们在很多项目的系统设计上花的时间很少(甚至没有),没有仔细思考和验证设计中隐藏的问题。基于这样的设计投入和设计质量,项目中很难避免设计失误。面对一个已经完成基本编码的项目,如果要“动大手术”去修改,相信有过类似经历的人一定知道那种感觉——越改越乱,你改变得越多,你就越焦虑。.在上述情况下,您是否有过类似的经历?2、研发前期投入多,收益更大关于软件研发,首先我们要树立一个很重要的观念。在研发的前期(需求分析和系统设计)投入更多的资源会比在研发的后期(编码、测试等)投入资源有更大的收益。为什么是这样?要回答这个问题,就需要从软件开发的整个生命周期的角度来考虑软件开发成本。除了编码,软件测试、上线、调试都需要很高的成本。如果我们把需求弄错了,与错误需求相关的设计、编码、测试、上线的成本就会被浪费掉;只是去浪费。如果你仔细考虑那些低效的项目,你会发现类似上面提到的“浪费”的地方还有很多。软件工程师看似很忙,但所有在错误方向上的努力并没有产生任何价值,而且大部分的加班实际上是在做错事,或者试图补救错误。在这种情况下,将更多的资源和注意力分配到研发的前期,立马就会有好的结果。3、修改代码和修改文档,哪个成本更高?很多软件工程师不愿意做需求分析和系统设计,是因为他们对“写文档”有根深蒂固的偏见。这里给大家提个问题。如果每个人都能正确回答这个问题,那么“写文件”的意识就会有很大的改变。没有人是能一下子把所有事情都做好的神仙。对于一个程序来说,必然要经历一定的修改和迭代。此时有两种选择:方案一:修改文档。在设计文档的同时完成迭代调整,没有大问题后开始编码。方案二:修改代码。只有一个粗略的设计文档,或者没有设计文档,直接开始编码,所有的迭代调整都在代码上完成。请判断,修改代码和修改文档哪个代价更大?在之前的一些分享会上,有人会说修改文档的成本比较高。因为修改文档后还要修改代码,所以多了一个过程。而直接修改代码只需要一次,更直接。这个回答说明受访者没有完全理解“先写文档,再写代码”的设计方法。如果对设计文档的工作不够重视,在输出的设计文档质量不高的时候就开始编码,确实会出现上述问题。但是,如果在设计文档阶段就已经进行了充分的考虑,将会减少代码的迭代和重复。同样的设计修改,“修改代码”的成本远高于“修改文档”。这是因为设计文档中只涉及到主要的逻辑,那些小而明显的逻辑不会出现在设计文档中。修改设计文档时,只有这些主要逻辑会受到影响。而如果对代码进行改动,不仅会涉及到这些主要逻辑,还会涉及到那些不会出现在文档中的小逻辑。对于一个程序来说,任何一个逻辑出现问题,程序都无法正常运行。4、需求分析和系统设计的区别很多读者分不清“需求分析”和“系统设计”的区别,所以会发现在书面文档中,有些系统需求出现在需求分析文档中。设计的内容,有的系统设计文档中夹杂着需求分析的内容。我们可以用几句话非常清楚地说明两者之间的区别。(1)需求分析:定义系统/软件的黑盒行为,即从外部(External)看到的行为,并解释“What”。(2)系统设计:设计系统/软件的白盒机制,是从内部(Internal)看的,需要解释“如何”(How)和“为什么”(Why)。例如,对于汽车,首先用户从外面可以看到车厢和车轮,坐在车内可以看到方向盘、刹车踏板、油门踏板等;方向盘可以改变汽车的行驶方向,制动踏板、加速踏板可以用于减速和加速。这些是汽车的“需求分析”。然后,我们想象汽车外壳和内饰变得透明,可以看到汽车内部的发动机、变速箱、传动杆、与刹车相关的内部装置等等。而这些对于司机来说是看不见的,它们就是汽车的“系统设计”。