当前位置: 首页 > news >正文

【软工方法论23】代码坏味道识别与消除

【软工方法论23】293_代码坏味道识别与消除

代码坏味道:识别与消除

你有没有这种感觉?

看一段代码总觉得哪里不对劲,但说不上来是哪里。

这就是代码坏味道(Code Smell)。

代码坏味道不是bug,而是代码中的"不对劲",预示着可能需要重构。

今天聊聊常见的代码坏味道和如何消除它们。

一、什么是代码坏味道?

代码坏味道:代码中的一些模式,暗示着设计或实现可能存在问题。

关键词

  • 不是bug,不会导致程序出错
  • 是"味道",暗示可能有问题
  • 需要经验来判断
  • 是重构的信号

生活比喻

  • 房间有异味,不一定是坏了,可能只是需要打扫
  • 代码有坏味道,不一定有bug,可能只是需要重构

“闻到坏味道,就该考虑洗个澡了——哦不,是重构了。”

二、常见坏味道及消除方法

1. 重复代码(Duplicated Code)

味道:同样的代码出现多次

// 坏味道classOrderService{publicvoidsendEmail(Orderorder){System.out.println("发送邮件到:"+order.getCustomer().getEmail());System.out.println("订单号:"+order.getId());System.out.println("金额:"+order.getAmount());}}classRefundService{publicvoidsendEmail(Orderorder){System.out.println("发送邮件到:"+order.getCustomer().getEmail());System.out.println("订单号:"+order.getId());System.out.println("金额:"+order.getAmount());}}// 消除:提取公共方法classNotificationService{publicvoidnotifyOrder(Orderorder){System.out.println("发送通知到:"+order.getCustomer().getEmail());System.out.println("订单号:"+order.getId());System.out.println("金额:"+order.getAmount());}}

2. 过长函数(Long Method)

味道:函数代码太长,超过一屏

// 坏味道:100多行的函数publicvoidprocessOrder(Orderorder){// 100行代码...}// 消除:拆分成多个小函数publicvoidprocessOrder(Orderorder){validateOrder(order);calculateDiscount(order);saveOrder(order);notifyCustomer(order);}privatevoidvalidateOrder(Orderorder){// 验证逻辑}privatevoidcalculateDiscount(Orderorder){// 折扣计算}

3. 过大类(Large Class)

味道:一个类做了太多事

// 坏味道:God ClassclassUserManager{// 100个方法,5000行代码// 用户管理、权限管理、统计分析、日志记录...}// 消除:拆分成多个类classUserService{// 用户核心业务}classPermissionService{// 权限管理}classUserAnalytics{// 用户统计}

4. 过长参数列表(Long Parameter List)

味道:函数参数太多,超过3个

// 坏味道publicvoidcreateUser(Stringname,Stringemail,Stringphone,Stringaddress,Stringcity,Stringcountry,intage,Stringgender,Stringprofession){// ...}// 消除:使用参数对象publicvoidcreateUser(UserDTOuserDTO){// ...}classUserDTO{privateStringname;privateStringemail;privateStringphone;// ...}

5. 发散式变化(Divergent Change)

味道:一个类因为不同原因需要修改

// 坏味道:数据库变了要改它,界面变了也要改它,逻辑变了还要改它classUserManager{publicvoidsaveUser(){/* 数据库逻辑 */}publicvoiddisplayUser(){/* 界面逻辑 */}publicvoidvalidateUser(){/* 业务逻辑 */}}// 消除:按职责分离classUserRepository{/* 数据库 */}classUserUI{/* 界面 */}classUserValidator{/* 验证 */}

6. 霰弹式修改(Shotgun Surgery)

味道:一个修改需要改多个类

// 坏味道:改用户名要改User、Order、Log、Notification...classUser{privateStringname;}classOrder{privateStringuserName;// 也要改}classLog{privateStringuserName;// 也要改}// 消除:使用ID而不是复制数据classOrder{privateLonguserId;// 用ID关联}classLog{privateLonguserId;// 用ID关联}

7. 依恋情节(Feature Envy)

味道:一个类更关心另一个类的数据

// 坏味道:UserService比User类本身还了解UserclassUserService{publicdoublegetUserScore(Useruser){intorderCount=user.getOrders().size();intrefundCount=user.getRefunds().size();doubletotalAmount=0;for(Ordero:user.getOrders()){totalAmount+=o.getAmount();}returntotalAmount/orderCount-refundCount*10;}}// 消除:把方法移到User类classUser{publicdoublegetScore(){// 这里面的逻辑更适合放在User类}}

8. 数据泥团(Data Clumps)

味道:一些数据总是同时出现

// 坏味道classOrder{privateStringstreet;privateStringcity;privateStringcountry;// 地址数据总是出现}classUser{privateStringstreet;privateStringcity;privateStringcountry;// 又出现了}// 消除:提取Address类classAddress{privateStringstreet;privateStringcity;privateStringcountry;}classOrder{privateAddressshippingAddress;}classUser{privateAddressaddress;}

9. 基本类型偏执(Primitive Obsession)

味道:过度使用基本类型,不愿用小对象

// 坏味道classUser{privateStringphone;// 用String存电话privateStringemail;// 用String存邮箱privateStringaddress;// 用String存地址}// 消除:使用值对象classPhone{privateStringnumber;publicbooleanisValid(){/* 验证逻辑 */}}classEmail{privateStringaddress;publicbooleanisValid(){/* 验证逻辑 */}}classAddress{privateStringdetail;}

10. Switch语句(Switch Statements)

味道:大量switch-case或if-else

// 坏味道publicStringgetGrade(intscore){switch(score/10){case10:case9:return"A";case8:return"B";case7:return"C";case6:return"D";default:return"F";}}// 消除:使用多态或MapprivatestaticfinalMap<Integer,String>GRADES=Map.of(10,"A",9,"A",8,"B",7,"C",6,"D");publicStringgetGrade(intscore){returnGRADES.getOrDefault(score/10,"F");}

11. 平行继承体系(Parallel Inheritance)

味道:每创建一个类,就要创建另一个相关的类

// 坏味道classWindowsButton{}classWindowsLabel{}classMacButton{}classMacLabel{}// 每加一个平台,要加一整套组件// 消除:使用组合classButton{privatePlatformplatform;}classLabel{privatePlatformplatform;}

12. 冗余类(Lazy Class)

味道:存在的价值很小的类

// 坏味道classOrderConstants{publicstaticfinalStringSTATUS_NEW="NEW";}// 只有几个常量,为什么要单独一个类?// 消除:合并到使用它的类中classOrder{publicstaticfinalStringSTATUS_NEW="NEW";}

13. 夸夸其谈的未来(Speculative Generality)

味道:为了"将来可能用到"而过度设计

// 坏味道interfaceAbstractItem{}interfaceAbstractOrder{}// "以后可能用到"classConcreteItemimplementsAbstractItem{}// 消除:现在需要什么就做什么classItem{}

14. 临时字段(Temporary Field)

味道:字段只在某些情况下使用

// 坏味道classOrder{privateStringnormalField;privateStringtempField;// 只在某个方法里使用publicvoidnormalMethod(){// 不用tempField}publicvoidspecialMethod(){// tempField = xxx;}}// 消除:把临时字段变成方法参数publicvoidspecialMethod(StringtempValue){// 不用tempField了}

15. 链式调用(Message Chains)

味道:长长的链式调用

// 坏味道Stringcity=order.getCustomer().getAddress().getCity().getName();// 消除:使用委托方法classOrder{publicStringgetCustomerCity(){returncustomer.getAddress().getCity().getName();}}

16. 中间人(Middle Man)

味道:类大部分时间都在转发调用

// 坏味道classUserManager{publicAddressgetUserAddress(LonguserId){returnuserRepository.findById(userId).getAddress();}publicPhonegetUserPhone(LonguserId){returnuserRepository.findById(userId).getPhone();}}// 消除:直接调用Useruser=userRepository.findById(userId);Addressaddress=user.getAddress();

三、坏味道速查表

坏味道典型特征消除方法
重复代码复制粘贴的代码提取方法/类
过长函数超过一屏拆分函数
过大类太多方法/属性拆分类
过长参数参数超过3个参数对象
Switch大量if-else多态/Map
数据泥团总是一起出现的数据提取类
基本类型偏执过度使用String/int值对象

总结

代码坏味道是重构的信号:

识别坏味道

  • 重复代码
  • 过长函数
  • 过大类
  • 过多参数
  • 深层嵌套

消除方法

  • 提取方法/类
  • 拆分大类
  • 使用设计模式
  • 参数对象化
  • 使用Map代替switch

“代码坏味道就像身体的不适——不一定会立即生病,但提醒你该注意健康了。”


思考题:你遇到过哪些代码坏味道?是怎么消除的?

http://www.gsyq.cn/news/1590700.html

相关文章:

  • 【无标题】AI API 聚合平台:大模型时代的一站式基础设施
  • Go语言的runtime.MemProfile中的诊断
  • 拆开宝珀五十噚Tech常驻款,这处机芯打磨让专柜销售闭嘴
  • 第三视觉理解徐玉生与他的商业活动(2)
  • 为什么NuGet下载量是.NET生态的晴雨表
  • 如何一站式解决Windows程序DLL缺失问题?VisualCppRedist AIO自动化工具全解析
  • 进程内套接字流转与无网路由仿真:基于 Flask 请求生命周期与 Requests 内存拦截的 Pytest 全链路微服务网络治理
  • Abode AN安装包
  • 从愤怒的小鸟到罗维奥:IP驱动型游戏公司的战略转型与运营实践
  • MacBook Air M2本地部署DeepSeek-Coder实战指南
  • 企业级智能体哪家做得好? 2026落地选型深度评测与架构实战
  • 人工智能专业术语详解(V)
  • 2026年触摸开关控制器口碑供应商推荐清单
  • 用了一个 AI 聚合平台后,我终于明白多模型入口的价值
  • 理查米尔中国官网价格的溢价骗局:拆开萧邦Happy Sport活动钻石,这处夹层让人瞬间清醒
  • 电商AI Agent开始参与售前服务,客服工作的重点正在发生变化
  • RAG系统从0到1
  • ROS2 Lyrical Luth 发布:Zenoh 替代 DDS,嵌入式开发者迎来机器人OS「轻量化革命」
  • 大语言模型(LLM)分类详解
  • 从零构建 DeepClassify:一个本地代码工程智能管理 Agent
  • [智能体-517]:AI 软件工程全流程工具(完整 SDLC 生命周期,2026 最新)
  • 使用subagent组建WPF视觉开发团队,全自动开发
  • 展筑沪上势能:2026上海靠谱展厅设计搭建公司深度实测梳理
  • 如何从Search Agent 方向,切入到 Coding Agent?
  • Elasticsearch介绍
  • IntelliJ IDEA离线安装全攻略(含JetBrains Toolbox替代方案):无网络环境下的3种纯净部署路径,企业IT管理员已批量验证
  • 你会亲手构建什么
  • HIP 编译器优化详解,ROCm 7.x 如何提升大模型推理效率
  • Leader 考核实习生:“你怎么配置 Claude Code?” 我挠头:“多写 Skills?” 她摇头:“明天别来了!”
  • Redis 缓存穿透与雪崩问题解决方案