【软工方法论23】代码坏味道识别与消除 📅 2026/6/26 2:54:34 【软工方法论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){intorderCountuser.getOrders().size();intrefundCountuser.getRefunds().size();doubletotalAmount0;for(Ordero:user.getOrders()){totalAmounto.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:returnA;case8:returnB;case7:returnC;case6:returnD;default:returnF;}}// 消除使用多态或MapprivatestaticfinalMapInteger,StringGRADESMap.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_NEWNEW;}// 只有几个常量为什么要单独一个类// 消除合并到使用它的类中classOrder{publicstaticfinalStringSTATUS_NEWNEW;}13. 夸夸其谈的未来Speculative Generality味道为了将来可能用到而过度设计// 坏味道interfaceAbstractItem{}interfaceAbstractOrder{}// 以后可能用到classConcreteItemimplementsAbstractItem{}// 消除现在需要什么就做什么classItem{}14. 临时字段Temporary Field味道字段只在某些情况下使用// 坏味道classOrder{privateStringnormalField;privateStringtempField;// 只在某个方法里使用publicvoidnormalMethod(){// 不用tempField}publicvoidspecialMethod(){// tempField xxx;}}// 消除把临时字段变成方法参数publicvoidspecialMethod(StringtempValue){// 不用tempField了}15. 链式调用Message Chains味道长长的链式调用// 坏味道Stringcityorder.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();}}// 消除直接调用UseruseruserRepository.findById(userId);Addressaddressuser.getAddress();三、坏味道速查表坏味道典型特征消除方法重复代码复制粘贴的代码提取方法/类过长函数超过一屏拆分函数过大类太多方法/属性拆分类过长参数参数超过3个参数对象Switch大量if-else多态/Map数据泥团总是一起出现的数据提取类基本类型偏执过度使用String/int值对象总结代码坏味道是重构的信号识别坏味道重复代码过长函数过大类过多参数深层嵌套消除方法提取方法/类拆分大类使用设计模式参数对象化使用Map代替switch“代码坏味道就像身体的不适——不一定会立即生病但提醒你该注意健康了。”思考题你遇到过哪些代码坏味道是怎么消除的