讲点码德!避免这些代码坏味道,努力做一名优秀的程序员

张开发
2026/4/15 22:52:43 15 分钟阅读

分享文章

讲点码德!避免这些代码坏味道,努力做一名优秀的程序员
Martin Fowler任何一个傻瓜都能写出计算机可以理解的代码。唯有写出人类容易理解的代码才是优秀的程序员。大家闭着眼睛想一下什么是好代码也许你的脑海中漂浮着一堆词干净、整洁、命名规范、注释合理、高内聚低耦合……人人都想写好代码因为看好代码就如同看一位五官端正的女子心情愉悦、舒畅而看糟糕的代码就如同看见腐烂的食物闻起来也有一股坏味道。大多数人写的代码都不能称之为好代码一方面由于自己技能限制另一方面也可能根本就分不清好代码和坏代码下面笔者结合日常编码实践与大家分享一下常见的代码坏味道。坏味道Long Method过长函数过长函数简而言之就是函数长度超标了包括横向和纵向。为什么过长函数是一种坏味道横向过长会导致无法一眼就能看出这行代码的作用需要用鼠标慢慢往后边拖相信用小屏幕的小伙伴经常会遇到这个问题拖动的过程会严重影响读代码的效率。纵向过长其实就是出现了大函数一个函数的行太多使得函数难以读懂代码修改难度大。那么如何解决过长函数问题呢关于横向过长的问题一般会在 IDE 中提前配置好最大宽度比如80字符或者120字符具体根据公司内部规范设置然后格式化代码即可解决。比如我们在写Java8 stream链式表达式的时候可以会很长ListString nodes list.stream().filter().filter().map.filter().collect(Collectors.toList()); // 可能会非常长其实我们可以在.之前换行这样看起来一目了然。ListString nodes list.stream() .filter() .filter() .map .filter() .collect(Collectors.toList());关于纵向过长的问题其实就是这个方法或者函数职责不够单一一个函数中堆积太多功能。重构的手段很简单Extract Method积极抽取函数或方法隐藏细节保持职责单一。坏味道Large Class过大的类过大的类也常常被成为上帝类God Class上帝类一般是指维护了太多功能违反单一职责原则连上帝也看不懂你的代码。知识小百科设计模式的六大原则有Single Responsibility Principle单一职责原则Open Closed Principle开闭原则Liskov Substitution Principle里氏替换原则Law of Demeter迪米特法则Interface Segregation Principle接口隔离原则Dependence Inversion Principle依赖倒置原则六个原则的首字母联合起来就是 SOLID两个 L 当成一个。那如何判断一个类是不是上帝类呢一般一个类同时满足以下3个条件就是上帝类1CPFD (Capsules Providing Foreign Data) 从多个不相关类模块中引用数据。2WOC (Weighted Operation Count) 类的所有函数的圈复杂度之和超过65。3TCC (Tight Capsule Cohesion) TCC 1/3 类需要具有低内聚的特性类中直接相关的方法与全部方法之比小于1/3也就是较少的private方法。为什么过大的类是一种坏味道过大的类承担了过多的职责往往有很多重复代码并且这些重复代码你还不容易发现这基本就是坏味道的开始。过大的类被子类继承会导致其他坏味道比如遗留的馈赠。如何解决过大的类这种问题呢通过观察这个过大类的属性看有没有一些属性有关联如果有可以使用Extract Class将这些关联属性抽象到一个新类中并将与这些属性相关的操作都Move到新的类中。通过观察这个过大类的方法看有没有一些函数或方法存在兄弟关联如果有可以使用Extract Subclass提炼子类的手段将这些方法提炼到子类中子类可以继承父类。将相似的行为方法聚集在一个类中拆分到多个类中可以进一步将发放调用解耦开。以上方法循环往复一个大类就可以拆分为多个小的且职责单一的类。坏味道Duplicated Code重复代码Robert C.Martin重复可能是软件中一切邪恶的根源。重复代码一般是由于复制粘贴造成的。需求迭代过程中为了不影响已有功能通常是将之前的代码copy一份改改然后匆匆上线。那为什么重复的代码是一种坏味道呢最直接的弊端就是如果你想修改一段代码逻辑可能会遗漏甚至需要多次修改才能确保全部修改完。如何解决重复代码的问题下面结合代码实践分几个场景分别描述。场景1同一个类中两个方法含有相同的表达式class A { public void method1() { logic1 logic2 logic3 } public void method2() { logic1 logic2 logic4 } }重构手段将两个方法共同的逻辑抽象出来。重构后class A { public void method1() { baseMethod(); logic3 } public void method2() { baseMethod(); logic4 } public void baseMethod() { logic1 logic2 } }场景2两个具有相同父类的子类内含有相同的表达式类 A 中有一个 method1有三段逻辑。class A extend Base { public void method1() { logic1 logic2 logic3 } }类 B 中有一个 method2也有三段逻辑。class B extend Base { public void method2() { logic1 logic2 logic3 } }重构手段将重复代码抽象成一个方法放在父类中差异部分由子类各自实现。重构后class Base { public void baseMethod() { logic1 logic2 } } class A extend Base { public void method1() { baseMethod(); logic3 } } class B extend Base { public void method2() { baseMethod(); logic3 } }场景3两个毫无相关的类出现重复代码如果两个没有直接关联的类出现重复代码可以考虑将重复的代码抽象到一个独立的普通类或者工具类中适用方可以使用组合的方式调用。代码样例这里不再赘述与场景1和2大同小异相信聪明的你一定能明白。坏味道Long Parameter List过长参数列全局变量是个邪恶的东西数据是共享的并且每个线程都可以修改太多了容易导致程序不可控所以大家喜欢将变量以行参的方式传递久而久之参数列越来越长了。为什么过长参数列是一种坏味道方法参数的数量太多会导致代码可读性非常差如果有多个重载方法它们的方法参数都非常多在写代码时很难判断该调用哪一个。当一个方法需要新增功能每次都可能会新增一个方法参数这样导致调用方每次都要重新适配小心被打哦耗子尾汁。如何解决过长参数列这种坏味道可以将多个参数封装到一个DTO 对象中方法间的对象通过对象的传输而不是过长的参数。数据传输对象DTO)(Data Transfer Object)是一种设计模式之间传输数据的软件应用系统。特别需要提醒的是有些情况下长参数也是合理的因为使用参数可以避免某些依赖关系的产生。在编码实践中我们可以通过观察长参数的方法如果这个方法经常变动那你就要考虑重构这个方法了。基本原则事不过三过三重构。坏味道Shotgun Surgery散弹式修改为什么散弹式修改是一种代码坏味道如果需要修改某个小功能你需要在众多不同的类中做修改首先很难找全其次很可能会遗漏这种问题一般称之为散弹式修改。public class A { Value(${db.mysql.url}) private String mysqlDbUrl; } public class B { Value(${db.mysql.url}) private String mysqlDbUrl; }假如有多个类都使用了db.mysql.url这个变量如果后面要将mysql切到Oracle那么可能会涉及到多处修改。如何解决散弹式修改这种代码坏味道呢可以使用Move Method搬移函数和Move Field搬移字段把所有需要修改的代码放进同1个类如果暂时没有合适的类就创建一个。坏味道Speculative Generality夸夸其谈未来性在工作中经常会听到有开发小伙伴说昨天加班我将订单模块做了修改未来可以……听到这里你可以会鼓掌牛叉啊提前对功能模板预留了扩展性。但是不要急于故障你看技术总监的脸黑着呢为什么呢这位小伙伴的代码可能是一种坏味道夸夸其谈未来性。为什么夸夸其谈未来性是一种代码坏味道互联网需求迭代更新速度快”未来可以“意味着当下并不需要有时候过度的抽象和预留扩展也会让系统难以理解并且可能提前背上包袱往前走。代码上总是谈未来可能性会让团队陷入泥沼。每次有业务变动开发人员都会考虑各种未来可能性预留足够多的扩展接口这无疑极大增加了代码复杂度让一个可能快速上线的需求变得慢下来。如何解决夸夸其谈未来性这种代码坏味道呢在代码架构设计中有一个原则叫Simple Design简单设计原则。当实现当下业务代码时需要考虑四个原则通过测试、揭示意图、消除重复、最少元素。当需要为未来而写的代码时可以干这些1删除那些觉的未来有用的参数、代码、方法调用。2修正方法名使方法名揭示当下业务场景的意图避免抽象的技术描述词。如果代码的改动确实是未来必然会发现的那么还是建议保留。夸夸其谈未来性更多是指开发人员无依据臆测未来导致代码模块被过度设计。坏味道Comments过多的注释在 《Clean Code》 中列举了一些常见注释坏味道喃喃自语多余的注释误导性注释循规方注释日志式注释废话注释用注释来解释变量意思用来标记位置的注释类的归属的注释注释掉的代码为什么过多的注释是一种代码坏味道呢好的注释可以辅助开发人员快速阅读理解代码过多的注释或坏注释可能会降低代码的可读性。在开发实践中经常有同学修改了代码但是注释没有同步修改代码的实现已经与注释内容不一致容易产生误导。如何解决过多的注释这种坏味道呢1如果代码块不再使用请直接删除不要使用注释。2方法、变量的命名尽量见名知意避免用注释再解释一遍。3如果较短的注释不能覆盖方法的含义可能是这个方法职责不单一可以考虑重构这个方法。总结文章列举了几种比较常见的代码坏味道希望大家在工作编码中多多练习争取人人都能写出好代码让天下没有难读的代码。如果需要深入学习代码坏味道推荐仔细研读两本书《重构:改善既有代码的设计》《Clean Code》。- END -

更多文章