同样的需求,每个人写出的代码看上去可能很不一样。大到模块的划分,中到类的抽象和组织,小到语句的写法,可能都不一样。一直在想有没有具体的指导方法可以让大家能写出大致相同的最优的代码。其实业界已经创造总结出来很多的指导原则,最佳实践,框架,工具等来使得大家写出好的代码。让我们从中看看怎么样才能写出专业的代码。
什么才是专业的代码?
概括的来说,专业的代码要准确,高效,易懂,易维护,易扩展。但是这种说法太宽泛了,不具备可操作性,所以我尝试总结可以让我们写出专业代码的方面。
为什么要写专业的代码?
程序的准确性和效率是最基本的要求,这是所有人最关心的,无论是测试人员,产品经理,管理人员还是程序员自己。这给程序员一种错觉,认为准确高效的实现了功能,就算圆满的完成了任务。即便程序员有意识想花点儿时间提升一下代码的质量,产品经理或者管理人员一般也会成为阻力,因为质量这个东西比较难于衡量,对产品经理来说也没有产出。
那为什么还要花精力写高质量的代码,把功能实现好了不就行了?其实大部分时间,程序员都是在跟旧代码做斗争,并且很可能是自己以前写的代码。如果代码清晰易懂,容易扩展,无论是修bug还是加新功能都会比较容易进行。
那么如何让代码专业,更确切一点,如何提高代码的可读性,可维护性和可扩展性。其实这三者之间不是孤立的,代码容易读懂,自然维护起来就容易了,在其上扩展新的功能也相对容易。但是它们又不完全等同。代码清晰易懂,也不见得扩展性就好。
要有合理的结构
就像盖房子,没有好的结构,砖砌的再好,可能最后的结果还是会垮掉。这里我们探讨的是代码层面的结构,而不是通常所说的架构,比如分布式架构,微服务架构等。结构涉及到职责,模块的划分等。如果职责清晰,模块独立,在较高的层次上,耦合性会低,复用性会高,结构清晰易懂,维护和添加新功能就会便利。
要有平台意识
通常来说程序一般包括平台代码和业务逻辑代码。平台代码是指可以被所有业务逻辑代码共享的公共服务,比如日志工具,transaction管理,页面路由,数据库连接管理,UI组件等。业务逻辑代码是指实现具体业务需求的代码。为了追求速度,程序员更倾向于直接实现具体的需求,而不是抽象出灵活可用的平台代码。这可能会造成大量重复代码,重复劳动,提高维护成本。
遵循业界的设计原则
我们以面向对象的Java语言为例。面向对象的设计,业界通常遵循SOLID原则。SOLID由五个英文单词的首字母组成:
S - 单一权责(Single Responsibility Principle)。模块,类,方法如果权责单一,可读性,可维护性必然提升。
O - 开闭原则 (Open / Closed Principle)。模块,类,方法要对扩展开放,对修改关闭。添加代码比修改代码要安全,这可以提高可维护性和可扩展性。
L - 李氏替换原则 (Liskov Substitution Principle)。面向接口编程,具体实现类可以替换。可以提升可扩展性。实现类可以替换为mock,很适合单元测试,从而提升了可维护性。
I - 接口隔离原则(Interface Segregation Principle)。接口中的方法要单一,从而降低耦合,增强可读性,可维护性。
D - 控制反转原则(Dependency Inversion Principle)。类依赖于抽象,不依赖于具体实现,并且把创建所依赖的对象的控制权交给其他模块。遵循此原则,可以使得代码降低对具体实现的依赖,降低耦合,提升可扩展性。对于Java开发者Spring Framework的IoC就是其中的一种实现。
遵循特定编程语言的最佳实践
每种编程语言都有属于自己的特性。针对特定的语言,往往都会有好的实践原则被总结出来。比如对于Java来说,Effective Java这本书中列举了90个实践原则。对于一个Java程序员来说,有必要好好研读一下。比如书中推荐使用Lambda,方法引用和Java类库中提供的方法接口,如果大家都遵循,那么大家写出来的代码就不会有的人用for循环,有的用Lambda,有的用自定义方法接口,有的用类库中的接口了。对于每种原则,不仅要会用,还应该知道背后的原因,从而可以灵活运用而不教条主义。比如大家可能都用过静态工厂方法,但是为什么不直接用构造函数?在什么情况下需要为类创建静态工厂方法?
使用大家熟知的模式
设计模式从实践中总结出来,被大家所熟知。使用好设计模式,代码一方面经的起检验,另一方面可读性会非常好,因为大家都熟知了,即便是不熟悉,相关的讲解材料也非常完善并且容易检索到。
对于Web应用来说,数据模型可以采用BO,DTO和VO的数据模型。它们分别是业务对象(Business Object),传输对象(Data Transfer Object)和视图对象(View Object)。BO可以转换为VO,是通用的,VO可以有多种格式,比如手机App和PC Web的VO。不同的VO格式可以共享相同的BO。这样在数据结构层面就做到了职责清晰易懂。
保持统一的代码风格
代码风格无需自创,很多大厂都公布了代码风格,同时开发工具,build工具也有相关的支持。比如国外有谷歌的Java开发手册,国内有阿里的Java开发手册等。
写整洁的代码
混乱的代码会极大削弱团队的生产力,而整洁的代码会带来长期的回报。《代码整洁之道》这本书在代码的各个方面都有如何做到整洁的阐述,比如类, 方法的命名,注释,错误处理,单元测试等等方面。建议好好研读和体会。
重构代码
好的代码不是一蹴而就的,因为业务逻辑是不断变化的,前面看似合理的代码,在添加了新功能就未必合适了。程序员对于业务逻辑的认知也是逐步深入的,这就需要对代码进行重构。不然代码会随着时间的推移渐渐腐败。
重构不是重写,小到改个易懂的变量名,大到把使用switch语句改造成使用多态都是重构。重构是在对业务和已有代码的认知逐步深入的过程中,为了提高代码的可读性,可维护性而对代码逐步整理的过程。每一步的重构都要保证代码的正确,重构是稳步向前的过程。每一步重构的结果可以加深对于代码的理解,甚至可以产生进一步重构的思路。举个不完全恰当的例子,重构过程就像是因式分解一样,需要逐步的进行。重构意味着改动旧代码,很多人可能会忌惮。其实只要按照特定的步骤进行,加上单元测试来验证,是安全的。《重构 改善既有代码的设计》这本书列举了六七十种的重构手法,基本能涵盖程序员写代码时遇到的场景,建议作为随时翻看的参考。
写好单元测试
单元测试主要有几方面的作用。一是验证代码的正确性,保证后续的改动不会破坏原先的功能,从而降低测试成本,加速开发过程,提高可维护性。二是帮助我们开发,我们可以用单元测试去学习和练习使用类库,在上到测试环境之前验证代码的正确性。三是可以更好的帮我们进行类的抽象和方法的定义。大家往往会以为编写单元测试会增加额外的时间,但是如果运用得当,其实能加速开发。但是我认为不能一味的追求覆盖率,二八原则还是很有道理的,用有限的时间换取最大的价值就可以了。
使用框架和工具
使用框架和工具可以使我们非常自然和便捷的写出专业的代码。比如Spring Framework的依赖注入模块可以让我们轻松的遵循SOLID中的D(控制反转)。Lombok插件可以让我们仅仅通过注解(Annotation)就可以实现Builder模式,静态工厂方法等。IntelliJ可以设置代码的格式,也支持一部分的重构手法(比如重命名,包装方法等)。
学习好的代码
现在是开源的世界。可以精读一些好的开源项目,一方面学习开源项目本身的技术,另一方面可以参考其他人的优秀代码。但是要想领会到代码好在哪里,需要有理论的支持,经验的沉淀。
我相信如果确立好方向,通过日常的积累和实践,最终就能写出专业的代码,从而减少程序员们深陷各种历史沼泽,和产品经理刀斧相加的概率,保卫好我们的发际线。
作者公众号(码年)扫码关注: