看脸的社会代码也要漂亮才行

图片1

漂亮整洁的代码不仅给人清爽舒适的感觉,也能有效提高程序员的工作效率,毫无疑问,在这个颜值担当的时代,代码也要看脸了,所以如何写得一手漂亮整洁的代码是程序员们特别需要get的技能。

代码整洁的大前提

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

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

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

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

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

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

说下比较适合写注释的两种场景:

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

设计模式只是手段,代码清晰才是目的

之前见过一些所谓“高手”的代码都比较抽象,各种工厂、各种继承。想找到一个实现总是要山路十八弯,一个工程里大部分的类是抽象类或者接口,找不到一两句实现的代码,整个读起代码来很不顺畅。我跟他聊起来的时候,他的主要立场是:保留合适的扩展点,克服掉所有的硬编码。

其实在我看来,也许他的代码被“过度设计”了。首先必须要承认的是,在同一个公司工作的同事,水平是参差不齐的。无论你用了如何高大上的设计,如果大多数人都不能理解你的代码或者读起来很费劲的话,其实这是一个失败的设计。

当你的系统内大部分抽象只有一个实现的时候,要好好思考一下,是不是设计有点过度了,清晰永远是第一准则。

代码整洁的常见手段

code review

很多大公司会用git的pull request机制来做code review。我们重点应该review什么?是代码的格式、业务逻辑还是代码风格?我想说的是,凡是能通过机器检查出来的事情,无需通过人。比如换行、注释、方法长度、代码重复等。除了基本功能需求的逻辑合理没有bug外,我们更应该关注代码的设计与风格。比如,一段功能是不是应该属于一个类、是不是有很多相似的功能可以抽取出来复用、代码太过冗长难懂等等。

我个人非常推崇集体code review,因为很多时候,组里相对高级的工程师能够一眼发现代码存在较大设计缺陷,提出改进意见或者重构方式。我们可以在整个小组内形成一个好的文化传承和风格统一,并且很大程度上培养了大家对clean code的热情。

勤于重构

好的代码,一般都不是一撮而就的。即使一开始设计的代码非常优秀,随着业务的快速迭代,也可能被改的面目全非。

为了避免重构带来的负面影响(delay需求或者带来bug),我们需要做好以下的功课:

  1. 掌握一些常见的“无痛”重构技巧,这在下文会有具体讲解。
  2. 小步快跑,不要企图一口吃成个胖子。改一点,测试一点,一方面减少代码merge的痛苦,另一方面减少上线的风险。
  3. 建立自动化测试机制,要做到即使代码改坏了,也能保证系统最小核心功能的可用,并且保证自己修改的部分被测试覆盖到。
  4. 熟练掌握IDE的自动重构功能。这些会很大程度上减少我们的体力劳动,避免犯错。

静态检查

现在市面上有很多代码静态检查的工具,也是发现bug和风格不好的比较容易的方式。可以与发布系统做集成,强制把主要问题修复掉才可以上线。目前美团点评技术团队内部的研发流程中已经普遍接入了Sonar质量管理平台。

代码整洁的常见技巧

单一职责

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

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

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

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

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

优先定义整体框架

我写代码的时候,比较喜欢先去定义整体的框架,就是写很多空实现,来把整体的业务流程穿起来。良好的方法签名,用入参和出参来控制流程。这样能够避免陷入业务细节无法自拔。在脑海中先定义清楚流程的几个阶段,并为每个阶段找到合适的方法/类归属。

这样做的好处是,阅读你代码的人,无论读到什么深度,都可以清晰地了解每一层的职能,如果不care下一层的实现,完全可以跳过不看,并且方法的粒度也会恰到好处。

简而言之,我比较推崇写代码的时候“广度优先”而不是“深度优先”,这和我读代码的方式是一致的。当然,这件事情跟个人的思维习惯有一定的关系,可能对抽象思维能力要求会更高一些。如果开始写代码的时候这些不够清晰,起码要通过不断地重构,使代码达到这样的成色。

清晰的命名

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

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

避免过长参数

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

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

避免过长方法和类

一个类或者方法过长的时候,读者总是很崩溃的。简单地把方法、类和职责拆细,往往会有立竿见影的成效。以类为例,拆分的维度有很多,常见的是横向/纵向。

例如,如果一个service,处理的是跟一个库表对象相关的所有逻辑,横向拆分就是根据业务,把建立/更新/修改/通知等逻辑拆到不同的类里去;而纵向拆分,指的是把数据库操作/MQ操作/Cache操作/对象校验等,拆到不同的对象里去,让主流程尽量简单可控,让同一个类,表达尽量同一个维度的东西。

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

这里想表达的是,尽量多地去抽取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 方法,就会舒服很多。

以上这些优化管理代码的方法能够有效使我们的代码看起来更清爽更漂亮,还是那句话,这绝不仅仅是颜值问题,这更是一个工作效率的问题,不想在冗杂的代码里挣扎就不妨尝试一下上面的方法吧,也许你会发现something amazing.

阅读原文


微信关注公众号:Aspose 满足一切文档需求

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值