衡量代码质量的唯一有效标准:WTF/min —— Robert C. Martin来源:公众号淘宝技术
衡量代码质量的唯一有效标准:WTF/min —— Robert C. Martin来源:微信官方账号淘宝科技
鲍勃大叔对好代码的理解很有意思,对我启发很大。我们写的代码不仅仅是用于机器执行产生我们预期的效果,更多的时候是被人阅读。这段代码可能会被后来的维护人员阅读,更常见的是在一段时间后被作者自己阅读。
我打赌大家都遇到过这样的情况:过了几个星期或者几个月,再次看到自己的代码,感觉一团糟,不禁怀疑自己的人生。
我们自己写的代码,过了一段时间还是老样子,更别说给别人看了。
任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码,才是优秀的程序员。—— Martin Fowler
所以,说到好的代码,你脑海里第一个蹦出来的词就是:整洁。
好的代码一定要整洁,给读者一种春风的感觉,赏心悦目。
整洁的代码如同优美的散文。—— Grady Booch
好代码的特征
很难给出好代码的定义。我相信很多人和我一样,并不认为整洁的代码一定是好代码,但是好代码一定是整洁的,整洁是好代码的必要条件。整洁的代码必须具有高内聚性和低耦合性,并且必须具有可读性和易维护性。
高内聚低耦合
高内聚低耦合是几乎每个程序员都会谈到的东西,但是这个词太宽泛太正确了,所以聪明的程序员提出了一些面向对象的设计原则来衡量代码的质量:
开闭原则 OCP (The Open-Close Principle)单一职责原则 SRP (Single Responsibility Principle)依赖倒置原则 DIP (Dependence Inversion Principle)最少知识原则 LKP (Least Knowledge Principle)) / 迪米特法则 (Law Of Demeter)里氏替换原则 LSP (Liskov Substitution Principle)接口隔离原则 ISP (Interface Segregation Principle)组合 / 聚合复用原则 CARP (Composite/Aggregate Reuse Principle)
这些原则想必大家都很熟悉,是我们写代码时的指导原则。按照这些原则开发的代码具有高内聚、低耦合的特点。换句话说,我们可以使用这些原则来衡量代码的质量。
但是这些原则并不是死板的教条,我们经常会因为其他的权衡(比如可读性和复杂性)而违反或者放弃一些原则。例如,当子类具有特征方法时,我们很可能会违反李希特替换原理。再比如,单一责任原则有时会和接口隔离原则冲突。我们通常放弃接口隔离的原则,保持单一责任。只要违背原则的理由充分,就不一定是坏代码。
可读性
代码高内聚低耦合就足够好了吗?不完全是。我认为代码也必须是可读的。好的代码应该在风格、结构和设计上是可读的。干净的代码可以从以下几个方面考虑,提高可读性。
名字
大到项目名、包名、类名,小到方法名、变量名、参数名,甚至一个临时变量的名字,其命名都是非常严肃的,需要考虑一个好的名字。
名副其实
好名字一定要名副其实,不用注释就能明白它的意思。
/* * *创建后的天数* */int d;int daysSinceCreation后者的命名比前者好很多,读者一下子就能明白变量的含义。
容易区分
我们很容易记下非常相似的方法名,仅仅通过名称(如getAccount()和getAccountInfo())无法区分,所以很难决定调用时用哪个,需要看实现的代码来确定。
易读的
名字一定要易读易读,最好不要用自己的缩写,也不要中英文混用。
足够短
当然,名字并不是越长越好,而是越短越好表达意思。
格式
好的代码格式也是提高可读性非常重要的一部分,可以分为垂直格式和水平格式。
垂直格式
通常一行中只写一个表达式或子句。一组代码代表一个完整的想法,不同组代码之间用空线隔开。
公开课演示{ @ Resource private List & ltHandler & gthandlerList私人地图& ltTypeEnum,Handler & gthandler map = new concurrent hashmap & lt;& gt();@ post construct private void init(){ if(!collection utils . isempty(Handler list)){ for(Handler:Handler list){ Handler map . put(Handler . gettype(),Handler);} } } publicResult & lt地图& lt字符串,对象& gt& gtquery(Long id,type enum type enum){ Handler Handler = Handler map . get(type enum);if(null = = handler){ return result . return failed(错误代码。CAN _ NOT _ HANDLE);} return handler . query(id);}}如果去掉空这一行,可读性会大打折扣。
公开课演示{ @ Resource private List & ltHandler & gthandlerList私人地图& ltTypeEnum,Handler & gthandler map = new concurrent hashmap & lt;& gt();@ post construct private void init(){ if(!collection utils . isempty(Handler list)){ for(Handler:Handler list){ Handler map . put(Handler . gettype(),Handler);} } }公共结果& lt地图& lt字符串,对象& gt& gtquery(Long id,type enum type enum){ Handler Handler = Handler map . get(type enum);if(null = = handler){ return result . return failed(错误代码。CAN _ NOT _ HANDLE);} return handler . query(id);类静态变量和实体变量应该在类的顶部定义。一个类内方法的定义顺序是:公共方法或保护方法>;私有方法> Getter/setter方法。
水平格式
应该有适当的缩进和空空格。
团队团结
通常情况下,同一个团队的风格尽量保持一致。该小组为Ja开发制定了非常详细的规范。(点击下方阅读原文,了解更多)
类别和功能
类和函数应该越来越短。
类和函数不能太长(集团要求函数长度最多不能超过80行)。过长的函数可读性一定很差,而且经常包含大量重复的代码。
函数只做一件事(在同一级别)
同一个函数的每个执行语句应该是一个统一的抽象层次。比如我们经常会写一个函数,需要给一个DTO赋值,然后调用接口,返回结果。那么这个函数应该包括三个步骤:DTO赋值、调用接口和处理结果。如果函数还包含DTO赋值的具体操作,那么这个函数的执行语句就不是同一层次的抽象。
参数越少越好
参数多的函数调用起来比较麻烦。尽量保持参数数量尽可能少,最好是没有。
给…作注解
不要注释糟糕的代码,重构它。
评论不能美化不好的代码。在尝试使用注释之前,考虑是否可以通过调整结构、命名和其他操作来消除编写注释的需要,这往往会使注释变得多余。
好的笔记提供信息,表达意图,解释和警告。
我们经常会遇到这样的情况:注释编写的代码执行逻辑与实际代码的逻辑不符。很多时候是因为代码改了,评论没有跟进修改。所以,最好提供一些代码没有的额外信息,表明你的设计意图,而不是写如何实现。
删除注释的代码。
git等版本控制帮助我们记录了代码变更的历史。没有必要保留过时的代码,带注释的代码也会干扰阅读。
错误处理
错误处理很重要,但不能搞乱代码逻辑。
错误处理应该集中在同一层,错误处理的功能不应该包含其他业务逻辑代码,只需要处理错误信息。
抛出异常时,提供足够的环境和说明,以便于故障排除。
抛出异常时,最好抛出类名、关键数据、环境信息等。这时候自定义异常类就派上用场了,通过统一层处理异常,可以方便快捷的定位问题。
特例模型可以消除异常控制或无效判断。
大多数异常来自NPE,有时这可以通过空对象来消除。
尽量不要返回null和传递null参数。
不返回null和不传递null也是为了最小化NPE的可能性。
如何判断不良代码?
讨论了好代码的必要条件。我们来看看好代码的负面条件:什么不是好代码。肯特·贝克用嗅觉来描述重构的时机。我觉得当代码有臭味的时候,也说明不是好代码。
糟糕的代码味道
重复
重复可能是软件中一切邪恶的根源。—— Robert C.Martin
马丁·福勒(Martin Fowler)也认为,臭味中首先是重复代码。
很多时候,当我们剔除了重复的代码后,发现代码比以前干净多了。
函数太长,类太大,参数太长。
过长的功能解释能力、分享能力、选择能力差,不容易维护。
一个类太大,意味着这个类做了很多事情,往往重复代码太多。
参数过长,难以理解,调用时容易出错。
发散性改变,散弹枪式改造,依恋情结
如果一个类不是单一的责任,不同的变更可能需要修改类,说明有发散的变更,不同的变更要考虑分开。
如果一个变更需要修改多个类的方法,就意味着有猎枪式的修改,要考虑把这些需要修改的方法放到同一个类中。
如果函数对某个类比对自己的类更感兴趣,说明有依恋情结,要考虑把函数转移到自己合适的类上。
数据泥球
有时您会发现三个或四个相同的字段出现在多个类和函数中。这时候就需要为这组字段建立一个类,并进行封装。
太多if…否则使用开关
太多的if…else或switch应该被多态替换。有些人甚至认为,除了少数情况,代码中不应该有if…else。
摘要
本文首先用一句话概括了好代码的必要条件:整洁,然后详细分析了整洁代码的特点,分析了好代码的负面条件:什么样的代码不是好代码?只是我自己的一些看法,希望对你以后的编程有所帮助。
我认为仅仅编写可行的代码是不够的。要时刻注意代码的整洁,留下一些漂亮的代码。希望所有写出来的代码都能保留运行102年!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。