24种常见的坏味道及重构手法

发布时间 2023-04-10 19:56:30作者: zhangyukun
  1. 神秘命名
    • 不能见名知意
    • 尽量去一个合适的名字
  2. 重复代码
    • 改动的时候很必须要全部找出,修改困难
    • 抽取公共代码,以便统一维护
  3. 过长的函数
    • 小读完所有行才知道清楚这个函数的大意,很多时候只需要知道这里面大概做了啥,不需要知道没一行做了啥,没行都读效率影响阅读效率,一般不要超过80行
    • 分段提取子方法
  4. 过长参数列表
    • 不便阅读,一般不要超过4个参数
    • 封装参数对象
  5. 过长的消息链
    • 类似于a.b.c.d.e这种
    • 在一个外观类里面提供一个xxE的方法,隐藏复杂的调用逻辑,后面a 只用和 这个外观类交互
  6. 全局数据
    • 容易被别人错误修改
    • 能不全局就不要全局,访问也小越好,如果有全局访问/修改的需求,可以考虑把这份数据放到对象的属性,然后通过对外提供方访问和修改。
  7. 可变数据
    • 址传递的时候,容易被别人误修改
    • 可以考虑不可变对象,final 关键字修饰,如果是构造参数可以考虑构造器模式
    • 全局数据和可变数据的都容易本别人修改,从而出问题,全局数据还有暴露信息太多的风险
  8. 发散式变化
    • 多种原因,都会修改同一段代码
    • 把多种变化的部分独立出来
  9. 霰弹式修改
    • 修改有一份功能,可能在很多份文件做小的调整
    • 可考虑把这些调整集中起来放到一个中介类里面管理
  10. 依恋情节
  • 类A的数据对类B的依赖比对类A还要高,比如汽车的动力只和发动机的型号有关,和汽车关系不大,所以马力应该 汽车.get发动机.get马力 而不是 汽车.get马力方法判断发动机型号)
  • 这时候我们应该考虑类A的这份数据是否可以放到类B去维护
  1. 泥团数据

    • 有时候两份数据是前关联的,其中一个出问题另一份数据就无法使用比如,地址的省市区,比如长方体的长宽高
    • 这种强关联的数据,最好提炼成为一个对象,而不是单独成为多个字段。
  2. 基本类型偏执

    • 比如坐标可以记作"x,y",但是坐标可能很多很多时候会拿到单独和横纵坐标x和y,也有可能需要格式化成(x,y)。比如金额5毛,可能需要显示成元,可能需要显示后面的货币符号。
    • 有具体含义,并且可能有其他伴生属性的对象,应该考虑封装成对象。
    • 可泥团数据类似,泥团数据强调多属性之间的依赖,基本数据偏执强调自身值的一些特性之间可能用对象表示更好,还可以隐藏细节。
  3. 重复的switch

    • 判断太多,不容易阅读,而且可能会写的很长
    • 使用策略,状态模式优化分支语句,把对应的逻辑写到各自的类里面去
  4. 简单的循环语句

    • 循环遍历,然后做一些简单的操作(过滤,分组,提取id,装换成别的对象,装换成Hash结构等)
    • 使用stream+lamda表达式可以很容易简洁的做到
  5. 冗赘的元素

    • 过度拆分的代码,比如一个一个日期格式工具,上面套了好几层,比如一个方法里面只要一行简单的方法调用。比如一段初始版本的计算现在已经完全弃用,比如已经注释的代码。这些东西都应该删除。
    • 这里更多是因为重构或者别的原因,导致其中一些方法已经失去原有的意义
  6. 夸夸其谈通用性

    • 和冗赘的元素元素类似,都是为了应对癔想的变化 过度封装,过度设计,写了很多结构复杂,预留拓展,实际可能几年了都没用上。
    • 这里强调过度设计的无效拓展性结构
  7. 临时字段

    • 大多是时候我们都会按照别的逻辑处理,但是有些特殊的情况,要在代码很多位置兼容这个特殊值
    • 我们可以考虑主服务程里面只写正常流程,特殊流程用另外一个特殊流程策略来处理,而不是把两者揉在一起。
  8. (过度使用)中间人

    • 如果a的方法里面存在大量直接简单包装调用a.b这种情况(超过一半,或者数量很多)
    • 这种时候应该提供getB,然后直接操作b,而不是总是在a里面去大量的包b的方法(如果少量这样干隐藏细节让人觉察不到b的存在是合理的)。
  9. 内幕交易

    • 和中间人相反,如果在a中直接返回b,这样b里面的方法修改,影响的地方会很多,但是如果在a里面包装b的的方法,影响的只有a。过度使用中间人和内幕交易 一个度的问题,描述的相反的情况。
  10. 过大的类(过于复杂的类)

    • 一个类里面有太多各自类型的属性,比如一个电脑,它只应该有8大件,不应该有具有主频,内存容量等信息,这些信息应该归属放到单独的组合类里面去,比如主频应该在 CPU上面,内存容量应该位于内存,电脑组合 cpu 和内存。
    • 面向对象设计原则类应该单一责任。
  11. 纯数据类(过于简单的类)

    • 如果一个类只有属性,没有方法,我们可以需要考虑是不是把应该属于这个类的行为放到了不合理的其他位置去了
    • 比如你有个电脑类,里面有8 大件,但是8 大件只提供了自己的属性,没有自己的对应的行为方法,所有的行为方法都在电脑这个类里面,8大类只是提供属性参数给电脑。这时候电脑这个类明显有问题,它变得极其臃肿,并且不便于拓展。
  12. 异曲同工的类(雷同的类)

    • 如果两个类有相同或者相识的功能,却有不同的定义,我们可以考虑是否真的需要两个这个的类,是否可以把他们合并起来?有些时候他们可能有意思,是些时候他们只会增加程序的复制度。
    • 比如你一个电脑插了两个鼠标(一般认为没意义),比如你的电脑接了2个显示器(一般认为有意义)
    • 比如你们系统只支持对公转账的大额支付,然后你订单以外设计了支付单(无意义),如果你们的系统需要微信,支付宝,银联支付等多种支付方式,然后订单以外设计了支付单(有意义)。
  13. 被拒绝的遗赠(基类属性行为冗余)

    • 继承实现这类父子关系的情况,如果父类继承下来的 属性或者方法,不是几乎所有子类通用的,那么后面这些子类的子类还要维护这些方法,这些属性还会被看到。
    • 这个不是太通用的属性或者方法可以考虑放到子类去,也可以考虑有别的接口和子类来管理这些接口或者属性
  14. 过多的注释的位置的代码

    • 注释是还好味道,但是当你需要写很长的注释来说明你在程序里面干了什么的时候,被注释额代码里面可能存在一些需要重构的怪味道。
    • 大量的注释的作用可能更应该用于描述为什么要这么干,以后打算怎么干,如果用于描述当前做了什么的注释太多,可能需要考虑代码结构是否应该优化