如何写出整洁的代码

为什么要写整洁的代码

为什么要写整洁的代码,回答这个问题之前,也许应该想想写糟糕的代码的原因

是想快点完成吗?还是要赶时间吗?有可能.或许你觉得自己要干好所需要的时间不够;假使花时间清理代码,老板就会大发雷霆.或许你只是不耐烦再搞这套程序,期望早点结束.或许你看了看,自己承诺要做的其他事情,意识到得赶紧弄完手上的东西,好接着做下一件工作.这种事情我们都干过.

"只要你干过编程,就有可能曾经被某人的糟糕代码绊倒过.如果你编程不止两三年,有可能被这种代码拖过腿.进度延缓的程度非常严重.有些团队在项目初期进展迅速,但是有那么一两年的时间却慢如蜗行.如对代码的每次修改都影响到了其他两三处代码.修改无小事.每次修改或添加代码都对那对扭纹柴了然于心,这样才能网上扔更多的扭纹柴.这团乱麻越来越大,在也无法清理,最后束手无策.

随着混乱的增加,团队生产力也持续下降,趋势余零.当生产力下降时,管理层就只有一件事情可做了:增加更多的人手到项目中,期望提高生产力.可新人不熟悉系统的设计.他们搞不清什么样的修改符合设计的意图,什么样的修改违背设计意图.而且,他们以及团队中的其他人都背负着提高生产力的压力.于是,他们制造了更多的混乱,驱动生产力向零的那端不断下降.

最后,开发团队造反了,他们告诉管理层,再也无法在这令人生厌的代码基础上做开发.他们要求全新设计.管理层不愿意投入资源完全重启炉灶,他们也不能否认生产力低得可怕.他们只好同意开发者的要求,授权去做一套看上去很美的华丽新设计.

于是就组建了一只新军.谁都想加入这个团队,因为它是张白纸.他们可以重新来过,搞出点真正漂亮的东西来.但只有最优秀,最聪明的家伙被选中.其余人则继续维护现有的系统.

现在有两支队伍在竞赛.新团队必须搭建一套新系统,要求实现旧系统的所有功能.另外,还得跟得上旧系统的持续改动.在新系统功能足以对抗旧系统之前,管理层就不会替换掉旧的系统.

竞赛可能会持续极长的时间.到了完成的时候,新团队的老成员已不知去向,而现有成员则需求重新设计一套新系统,因为这套系统太烂了.

假如你经历过哪怕是一小段我谈到的这种事,那么你一定知道,花时间保持整洁的代码不但有关于效率,还有关于生存. "

有时我们抱怨需求变化背离了初期设计.哀叹进度太紧张,没法好好干活.我们把问题归咎于那些愚蠢的经理,苛刻的用户,没用的营销方式.

经理和营销人员指望从我们这里得到必须的信息,然后才能做出承诺和保证;即便他们没开口问,我们也不该羞于告知自己的想法。用户指望我们验证需求是否都在系统中实现了。项目经理指望我们遵守进度。我们与项目的规划脱不了干系,对失败负有极大的责任:特别是当失败与糟糕的代码有关时尤为如此!
“且慢!”你说。“不听经理的,我就会被炒鱿鱼。”多半不会。多数经理想要知道实情,即便他们看起来不喜欢实情。多数经理想要好代码,即便他们总是痴缠于进度。他们会奋力卫护进度和需求;那是他们该干的。你则当以同等的热情卫护代码。

再说明白些,假使你是位医生,病人请求你在给他做手术前别洗手,因为那会花太多时间,你会照办吗?本该是病人说了算;但医生却绝对应该拒绝遵从。为什么?因为医生比病人更了解疾病和感染的风险。医生如果按病人说的办,就是一种不专业的态度,更别说是犯罪了。同理,程序员遵从不了解混乱风险的经理的意愿,也是不专业的做法。我们应该加强这方面的意识。

程序员面临着一种基础价值谜题。有那么几年经验的开发者都知道,之前的混乱拖了自己的后腿。但开发者们背负期限的压力,只好制造混乱。简言之,他们没花时间让自己做得更快!真正的专业人士明白,这道谜题的第二部分说错了。制造混乱无助于赶上期限。混乱只会立刻拖慢你,叫你错过期限。赶上期限的唯一方法一做得快的唯一方法就是始终尽可能保持代码整洁。

“如果你不明白整洁对于代码有何意义,尝试去写整洁代码就会毫无所益.”

截取自《代码整洁之道》

什么是整洁的代码

每个人对于整洁的代码理解肯定不同,在我看来,满足业务场景的情况下,可读性强,运行效率高,细节处理好,易扩展的代码就是整洁代码. 抛开业务场景不谈,只谈所谓的"整洁代码"就是所谓的耍流氓。

整洁的代码总是看起来总是像是某位特别在意它的人写的.几乎没有改进的余地.代码的作者什么都想到了.如果你想改进它,总会回到原点。

代码的作用是为了解决人们的某种需求,什么语言不重要(但是总有人非要争个高低),问题解决了才重要.在规定的业务场景下,写出能解决用户需求的代码就是程序员的日常工作,而需求并不是一成不变的,需求会变代码也会改变,所以我们就需要再这个特定的业务场景中尽量把代码变得灵活起来,之后增加需求或者修改需求时,会变的容易一些。

至于方法的规范命名,可读性,注释等.这些也很重要,毕竟开发的时候一般都是团队一起来开发,代码不止你自己看,还需要别人看,说简单一些就是别人好接手你的代码,即代码的维护

可读性

在一个产品的周期中,开发其实只占了一小段时间,绝大多数时间都在维护代码.代码写出来首先是给人看的,其次是给电脑看,所以代码的可读性至关重要.所以,如果我非要在代码的可读性和运行效率之间选择一个,非可读性莫属.一般来说,要权衡代码的可读性和运行效率,如果差距太大,要看实际的业务场景来决定,毕竟写程序的最终目的是为了解决用户的某些问题.

可读性通常表现在代码易于理解

  • 容易理解整个应用程序的执行流程
  • 容易理解不同对象之间的协作方式
  • 容易理解每个类的作用和责任
  • 容易理解每个方法的作用
  • 容易理解每个表达式和变量的目的是什么

如果一个方法的行数过多也会影响代码的可读性,一般控制在80行左右。过多无用的注释、API,只会加重使用者的认知负担,过多无用的信息读起来只会浪费时间,所以要尽量保持API的精简,代码注释的合理,保持规范的命名,使注释看起来没那么臃肿。要知道代码有人维护,可注释没有人维护。

代码依赖性导致了变化的放大和高认知负荷。模糊性造成了未知的不可知性,导致了认知负荷。从而使得代码更加难以理解,从而不能很好的维护. 所以整洁的代码总是复杂性低的。

运行效率

运行效率即代码的运行效率,包括运行所占用的时间和空间。如果数据量不是很大(单表在300w左右)可能几乎不用考虑这个问题,空间就更不用说了,现在大多数公司都是用空间来换时间的,即通过增加服务器的配置或数量来提高程序运算速度。所以很多人并不关心程序运行的效率。

诚然,我也不是很关心软件的运行效率,因为软件的运行效率主要还是取决与硬件的发展水平,现在硬件发展比软件发展快了一个档次,不然现在也不能一下子涌起那么多的软件公司。

但是,如果业务量非常大,电脑的运行效率也是有限的,当服务器达到一定数量后,企业就会考虑成本毕竟不能一直毫无节制的增加下去,这时候就需要考虑程序的运行效率了。

作为一个好的程序员,你不得不具备这项技能。

怎样提高程序的运行效率,有没有想过?程序是算法和数据结构组成的,数据结构决定一个程序的空间复杂度,算法则决定一个程序的时间复杂度。 想要程序跑的更快,空间占用更少,可以从这两个维度来进行探索。

一个好的算法离不开一个好的想法,这对于一个程序来说是至关重要的,因为它是决定了程序运行速度的关键原因。可能很多人都有一个误区,就是代码越少执行效率就越高,在改进算法的时候会通过删减代码来进行。举个例子:匹配字符串,在数据量很大的情况下,暴力匹配的方式无论你怎么改,都会比那些运用了好的算法的程序慢。

不整洁的代码是混乱的,代码混乱到一定程度就会对程序的运行速度产生影响。所以,代码的整洁程度一定程度上影响了代码的运行速度。

扩展性

整洁的代码除了是可读性强、运行效率高还有最重要的一点是它是容易扩展的。扩展性可理解为易于修改的代码。程序的扩展性代表了维护该程序程度的难易,当然可读性也是,二者都很重要。

在所有的设计模式中,几乎所有的设计模式都是为了符合开闭原则,即保持程序的扩展性,重要程度可见一斑。

代码都是为了一定的需求服务的,但是这些需求并不是一成不变的,当需求变更了,如果我们代码的扩展性很好,我们可能只需要简单的添加或者删除模块就行了,如果扩展性不好,可能所有代码都需要重写,那就是一场灾难了,所以提高代码的扩展性是很重要的。

衡量代码扩展性可以从高内聚,低耦合这两个方面来衡量。

高内聚:一个软件单位内部的关联紧密程度;有关联的事务应该放在一起。
低耦合:两个或多个软件单位之间的关联紧密程度;软件单位之间尽可能的不要相互影响。

怎么写整洁的代码

好的代码是不断的迭代出来的,没有人能一下子写完整 ,需求会变代码也会改变 第一次迭代可能写的代码很糟糕,这时一定要再次回头去看之前的代码,去优化,重构,让代码变得易于维护.

如何写出整洁的代码,那就看你怎么理解整洁的代码,理解的不一样写出来的肯定就不一样.下面是我的几点建议

注释&命名

注释只是二手的信息,它和代码所传达的信息并不等价.所以,不要写没有意义的注释(冗余注释,废弃注释,等一些没有意义的信息和不恰当的信息),要知道代码有人维护,可注释没有人维护,最好的办法就是规范变量,方法的命名,做到见名知意; 如果你的方法命名足够明确就可以不用写注释了,当然一段好的注释一定是包含代码中没有体现的信息。

如果要编写一条注释,就花时间尽量保证写出最好的注释 不要画蛇添足,要保持注释的简洁。比如,无用的代码直接删掉,不要注释它,不用担心会丢,版本服务会有记录能找回。编写注释和迭代代码是一样的道理。但是一般注释是没有人来维护的,因为它不会影响程序的正常运行。

同样的,命名也不会影响程序的正常运行。注释和命名是不会影响程序的执行的,但是这两个因素是会影响到开发者编写代码的。它会导致开发者的认知负荷增加,从而降低编写代码的效率。

“ 好的代码本身就是注释,我们要尽量规范和美化自己的代码来减少不必要的注释。若编程语言足够有表达力,就不需要注释,尽量通过代码来阐述。”编程语言的表达力很大程度上就取决于方法的命名。

那什么是好的命名呢?说实话这也是我编程一直头疼的原因,总觉得命名不够好。在网络上看到一种方法觉得很不错: 把你的变量名拎出来,问别人,你看到这个名字会想到什么,他说的和你想的一致,就用,否则就改。改到基本上不懂程序的人都能大概看懂你写的是什么,那大概就是好的命名了。

程序的命名我认为是约定俗成的,最初开始编程的开发者们,他们一定会遇到这个问题的,久而久之就会建立一套规则将这些命名进行统一。到了现在一定是有很多成熟的命名规则的,所以我们可以踩着前辈们的肩膀前行。

虽然是约定俗成的,但是事物的发展一定不是一成不变的,我并不知道在我写下这么多文字后是否适用于未来,或许大概只有思想会适用,而那些具体的方法是一定不会适用的,但是现在我们需要具体的解决方案,或许我能给你提点建议,如果能帮到你我会很高兴的.

注释

  • 尽量保持代码的简洁,能不用注释尽量不用注释,切记注释应该展示代码中没有展示的信息
  • 注释代码,那么应该在注释代码的上方详细的说明,而不是简单的注释,如果没有用则应该删除
  • 注释也要随着程序的修改而不断更新,一个误导的注释往往比没有注释更糟糕

命名

  • 命名尽量不要使用缩写,因为意义不明确,除非缩写是外界公认的,比如: EN,CH
  • 在Java中较为适用的是驼峰命名法,如: helloWord,HelloWord ,但是这种方法如果命名过长也不能很直观的展示信息,所以尽量不要起太长的名字,否则什么方法都不管用,如果一个方法的名字过长那么很可能这个方法不止做了一件事,这时候我们需要将它拆分.
  • 接口和类的名称首字母大写(MyClass/MyInterface) ,方法的名称首字母小写(myMethod),常量的命名全部大写(MY _CONSTANT)同时用蛇形命名法,单词的分割用下划线隔开
  • 方法的命名用动词,类的命名用名词,属性的命名用名词,接口的命名用形容词或动词,抽象类的命名应以Abstract/Base为前缀,实现类命名要以impl结尾,异常类以Exception结尾

函数&类

最好的方法入参参数是0个,其次是1,最多建议不超过3个,大于三个建议封装成对象,这样做的好处是方便扩展管理;在一个方法中声明变量应该在方法的最前面,我们应该降低方法的复杂度,避免出现if…else多层嵌套的情况,当然,如果一个方法过于复杂可将该方法进行拆分;还应当注意方法中异常块的处理,一般情况下是不会写的,因为有全局异常处理,如果非要写那么可能代码看起来不是那么简洁清晰.注意方法的封装.在方法调用本地方法的时候,本地的方法尽量使用基础类型作为返回值,好处是避免空指针判断和隐式拆包和打包.当我们写方法返回值的时候,如果返回值类型是数组或集合,我们应该避免返回null,否则调用方就有可能出现空指针.可以避免不必要的空指针判断。

或许我应该更加细致的标出

  • 减少函数的入参数量,控制在三个以内,超过三个封装成对象
  • 使用卫语句,策略模式,职责链模式来减少if…else多层嵌套和不必要的else;用三元运算符代替简单if…else(根据不同的情境使用,可能会影响代码的可读性)
  • 拆分超长函数(函数行数阈值80~100行左右就要考虑拆分);复杂的条件表达式,循环语句,代码块,lambda匿名内部类,都可以单独将其封装成方法;如果是方法内部调用方法的返回值应尽量用基础类型 (并不是方法越多越好,方法之间的互相调用也会影响性能,增加复杂度,请根据实际情况拆分)
  • 方法的返回值如果是集合或者数组,不要返回null,尽量返回空值,这样可以避免空指针的判断,从而精简代码

除了上述的几点以外,在写函数的时候还要遵循单一职责原则,即每个方法只做一件事,好处是方便管理,代码可读性会提高,复杂度降低 易于维护。也要遵循开闭原则,这会使你的代码更加灵活。当然这些代码肯定不是一次就写出来的,好的代码需要迭代,需要打磨。在你写完几个函数之后,可能会发现重复的地方,这时候就需要将他们抽象出来。

许许多多的函数组成了类,当函数之间相互调用的时候,就会存在多个类之间的联系,所以在编写类的时候要注意类之间的依赖关系,使它们别那么耦合,一般会遵循迪米特法则。

属性的存在使类中的元素更加丰富,一般情况下属性在类中都是私有的,会对外提供set,get方法供外部调用修改;这样做的好处是方便控制外部调用,假如你想公共处理某个属性给它加个前缀,就可以通过调用该类中涉及到该属性的方法进行修改,如果你直接修改属性那么改起来会很麻烦。

极少数情况是公共的,比如定义一个常量类,公共的资源属性。还有多数情况下是受保护的,该种情况一般是用来给子类使用的,当然同一个包下也能访问得到。

代码结构

高内聚低耦合,这是我们写代码应该遵循的标准。内聚代表着职责的专一,这是整洁的一个很重要准则。从大的方面来说,系统的使用与构造需要明确的区分开,才不会将整个结构混杂在一起。与此同时,决定了系统的数据流走向也是决定了整个系统的层级划分,不同的层级也需要明确的区分开来。

那么应该怎么划分代码的结构?最简单的应该同类型的相关联的表需要放在一个类中或一个包中,写一些方法对外提供api,供其他方法调用,而不是跨层调用。

一个好的结构使代码看上去更加清晰,更加容易维护,其实它更像是对系统架构的拆分。最常见的系统分层应该是MVC结构,即模型层、视图层、控制层。我们应当更加详细的将MVC进行划分,最常见的我们将控制层又分为业务层(service)和持久层(dao)。 划分的目的是规划软件系统的逻辑结构便于开发维护。但是,随着微服务的演变和不同研发的编码习惯,往往导致了代码分层不彻底导致引入了“坏味道”。

划分代码我认为最重要的作用是使结构单一,减少代码之间的依赖性,降低耦合度,从而提高代码的可维护性。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值