推荐《Clean Code》一书,让你成为优秀的程序员

时间:2023-02-11 23:05:25

曾经维护过有十多年历史的银行系统,也全新开发过多模块的企业应用系统。经历过各种各样写法的代码,有的难以维护,有的清晰明了,有的埋下深坑… 在我的团队里,我总是向新人灌输代码整洁之道的思想,我时刻告诉他们,功能完成只是最基本的要求,更重要的是你能把代码清晰明白地表达出业务逻辑,别人容易看得懂你写的代码。
对于提升代码构建水平,Clean Code - 代码整洁之道,好书值得推荐一读。


下面是摘自美团技术团队作者的部分归纳,作者的思想和我看完书后的感觉完全一致,分享给大家:

clean code,顾名思义就是整洁的代码,或者说清晰、漂亮的代码,相信大多数工程师都希望自己能写出这样的代码。

也许这是个千人千面的话题,每个工程师都有自己的理解。比如我,从一个天天被骂代码写得烂的人,逐渐学习成长,到现在也能写的出“人模人样”的代码来了。这期间算是积累了一点经验心得,想和大家分享,抛砖引玉。

本文主要针对面向对象编程的clean code来阐述,面向过程代码的思路会比较不同,不在本文的讨论范畴。


代码大部分时候是用来维护的,而不是用来实现功能的

这个原则适用于大部分的工程。我们的代码,一方面是编译好让机器执行,完成功能需求;另一方面,是写给身边的队友和自己看的,需要长期维护,而且大部分项目都不是朝生夕死的短命鬼。

大部分情况下,如果不能写出清晰好看的代码,可能自己一时爽快,后续维护付出的代价和成本将远高于你的想象。

对清晰好看代码的追求精神,比所有的技巧都要重要。


优秀的代码大部分是可以自描述的,好于文档和注释

当你翻看很多开源代码时,会发现注释甚至比我们自己写的项目都少,但是却能看的很舒服。当读完源码时,很多功能设计就都清晰明了了。通过仔细斟酌的方法命名、清晰的流程控制,代码本身就可以拿出来当作文档使用,而且它永远不会过期。

相反,注释不能让写的烂的代码变的更好。如果别人只能依靠注释读懂你的代码的时候,你一定要反思代码出现了什么问题(当然,这里不是说大家不要写注释了)。

说下比较适合写注释的两种场景:
public interface,向别人明确发布你功能的语义,输入输出,且不需要关注实现。
功能容易有歧义的点,或者涉及比较深层专业知识的时候。比如,如果你写一个客户端,各种config参数的含义等。


代码整洁的常见技巧

1.单一职责

这是整洁代码的最重要也是最基本的原则了。简单来讲,大到一个module、一个package,小到一个class、一个method乃至一个属性,都应该承载一个明确的职责。要定义的东西,如果不能用一句话描述清楚职责,就把它拆掉。

我们平时写代码时,最容易犯的错误是:一个方法干了好几件事或者一个类承载了许多功能。

先来聊聊方法的问题。个人非常主张把方法拆细,这是复用的基础。如果方法干了两件事情,很有可能其中一个功能的其他业务有差别就不好重用了。另外语义也是不明确的。经常看到一个get()方法里面竟然修改了数据,这让使用你方法的人情何以堪?如果不点进去看看实现,可能就让程序陷入bug,让测试陷入麻烦。

再来聊聊类的问题。我们经常会看到“又臭又长”的service/biz层的代码,里面有几十个方法,干什么的都有:既有增删改查,又有业务逻辑的聚合。每次找到一个方法都费劲。不属于一个领域或者一个层次的功能,就不要放到一起。

我们team在code review中,最常被批评的问题,就是一个方法应该归属于哪个类。

2.清晰的命名

老生常谈的话题,这里不展开讲了,但是必须要mark一下。有的时候,我思考一个方法命名的时间,比写一段代码的时间还长。原因还是那个逻辑:每当你写出一个类似于”temp”、”a”、”b”这样变量的时候,后面每一个维护代码的人,都需要用几倍的精力才能理顺。

并且这也是代码自描述最重要的基础。

3.避免过长参数

如果一个方法的参数长度超过4个,就需要警惕了。一方面,没有人能够记得清楚这些函数的语义;另一方面,代码的可读性会很差;最后,如果参数非常多,意味着一定有很多参数,在很多场景下,是没有用的,我们只能构造默认值的方式来传递。

解决这个问题的方法很简单,一般情况下我们会构造paramObject。用一个struct或者一个class来承载数据,一般这种对象是value object,不可变对象。这样,能极大程度提高代码的可复用性和可读性。在必要的时候,提供合适的build方法,来简化上层代码的开发成本。

4.过长方法和类

一个类或者方法过长的时候,读者总是很崩溃的。简单地把方法、类和职责拆细,往往会有立竿见影的成效。以类为例,拆分的维度有很多,常见的是横向/纵向。例如,如果一个service,处理的是跟一个库表对象相关的所有逻辑,横向拆分就是根据业务,把建立/更新/修改/通知等逻辑拆到不同的类里去;而纵向拆分,指的是把数据库操作/MQ操作/Cache操作/对象校验等,拆到不同的对象里去,让主流程尽量简单可控,让同一个类,表达尽量同一个维度的东西。

5.让相同长度的代码段表示相同粒度的逻辑

这里想表达的是,尽量多地去抽取private方法,让代码具有自描述的能力。举个简单的例子

 public void doSomeThing(Map params1,Map params2){
   Do1 do1 = getDo1(params1);
   Do2 do2 = new Do2();
   do2.setA(params2.get("a"));
   do2.setB(params2.get("b"));
   do2.setC(params2.get("c"));
   mergeDO(do1,do2);
   }
   private void getDo1(Map params1);
   private void mergeDo(do1,do2){...};

类似这种代码,在业务代码中随处可见。获取do1是一个方法,merge是一个方法,但获取do2的代码却在主流程里写了。这种代码,流程越长,读起来越累。很多人读代码的逻辑,是“广度优先”的。先读懂主流程,再去看细节。类似这种代码,如果能够把构造do2的代码,提取一个private 方法,就会舒服很多。


以上列出的是最基本要求的扎实技能。

更详尽请点击:http://tech.meituan.com/clean-code.html

本文属转载,请尊重原著。
原创2017-01-19王烨美团点评技术团队美团点评技术团队
联系邮箱:wangye03@meituan.com