重构 改善既有代码的设计——代码的坏味道

了解并熟练重构的机制,并不代表我们知道何时应该重构;前者可以从书中学习到,但是何时应该重构就是一个实践问题,需要我们 培养自己的判断力,知道何时何处如何重构;

1.Duplicated Code(重复代码)

A.同一个类中不同方法包含相同的表达式:把相同的表达式提炼成一个方法,在包含表达式的地方直接调用该方法;

B.互为兄弟的类包含相同的表达式:把相同的表达式提炼成一个方法,并放到基类中;

C.两个互不相干的类包含相同的表达式:C1::重复代码属于某一个类,抽象出方法,另外的类只能引用它;C2:该重复代码属于第三类,另外两个类只能引用该类;具体该方法放到哪里合适,需要自己确定,但要保证一旦确定该方法就不会再出现在其他的地方;

评:分工明确,接口化合作


2.Long Method(过长方法)

A.方法分解标准:倘若在一个方法中,我们感觉要注释来说明某一行/段代码时,我们就需要把要注释的代码抽象成独立方法,并以用途为其命名;

评:java中对类的定义是“具有相同属性和功能”的对象,这里的功能指的就是方法。每一个方法只实现一个功能,如此方法本身就是注释;哪怕是一行代码或修改后的方法调用动作比自身还要长,我们也要毫不犹豫的去做。我们定义函数不是根据函数的长度,而是根据“做什么”和“如何做”之间的语义距离定义的;


3.Large Class(过大的类)

如果想要在一个类中做太多的事情,难免会导致创建很多临时变量,同时Duplicated Code(重复代码)也就肯定在所难免;为此,有以下解决方案:

A.提炼变量:将相关的变量提炼到同一个类中,方便变量的管理

B.提炼方法:将重复的表达式提炼成独立方法,以供调用;

评:一个类犹如一个组织结构,如果分工不清,就会导致调用过多的成员,但是成员的管理却很糟糕;最好的方式就是金字塔式管理;


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

A.参数过长,会导致理解困难,修改麻烦,管理混乱;

B.以对象的形式封装相关的参数,倘若参数不相关,那就封装到一个对象中;

C.若不想某一参数和对象产生依赖关系,那就只能以参数的形式输入了;


5.Divergent Change(发散式变化)

A.一个类受多种变化的影响:如果某个类经常因为不同原因在不同方向变化,也就是说类的职能不专一,往往会导致发散式变化;这种情况下最好的方式就是把经常变化的模块抽象成一个独立类,保持类的功能的单一和单纯性;

评:类就像人,一个类同时做两件或多件事,就好比如人脚踏两只船,迟早会翻的;人且不能同时做两件事,何况程序乎!


6.Shotgun Surgery(霰弹式变化)

A.一种变化引起多个类相应修改:如果每遇到一种变化,程序都需要在各处修改,这就是霰弹式变化了;这个时候就需要把需要修改的代码放到一个类中;

评:此处运用的还是“中间件”,把所有变化提炼出来一个独立模块,这也要求我们在写接口的时候应该考虑到可能发生的变化,要尽量保证接口的不变性;只修改功能的实现,以不变应万变;


7.Feature Envy(依恋情结)

A.方法的位置不当,导致从其他类中引用很多参数,这就是所谓的依恋情结;

评:面向对象的技术的核心即是“将数据和对数据的操作放到一起”,这也正是“类”的定义:“具有相同属性和方法的对象”;所以当我们遇到这种情况时,首先就要考虑的就是把方法和它所调用的最多数据的类放到一起;也就是说方法是操作数据的,应该和数据关联,如果方法在逻辑位置脱离数据,这是背离了面向对象的定义了的;


8.Data Clumps(数据泥团)

A.在实际的开发中,经常会有这种体会:在多个类中会重复调用多个变量;此时,我们需要给这些变量生成一个对象;把这些经常同时出现的变量归纳到类中;一个判断是否归纳的标准就是如果把某个参数遗弃,,其他数据都会失去意义;比如:定义人从身高、性别、年龄等考虑,如果某一属性缺失都无法完整地定义“人”;

评:类就像一个家庭,类中的变量就是家庭中的成员;数据泥团就相当于失散的家庭,各处都有家人(不同的是数据泥团同一个变量可以重复);


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

此处理解不是太深,略...


10.Switch Statement(switch事件)

A.面向对象的一个特性就是多态,这也就意味着少用switch方法(或case事件),如果类中出现了switch,最好抽象到独立的方法内,然后根据需求把方法放到合适的位置;


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

A.当为某一个类增加一个子类时,同时也需要为另外一个类增加一个子类;如果某一继承体系的类名前缀和另一继承体系类名前缀相同,你就闻到了坏味道了;

B. 消除这种重复性的一般策略是:让一个继承体系的实例引用另一个继承体系的实例,如果再接再厉使用Move Method和Move Filed方法就可以将引用端的继承体系消除于无形;

评:不是太理解,还请高人指点;


12.Lazy Class(冗余类)

A.在开发的过程中出现有些类在开始的时候使用很多,但随着重构的推进,这些类慢慢的就失去了一开始存在的意义,此时应该毫不犹豫的把它们删除掉;

评:该出手时就出手,没有用的就及时丢弃,避免浪费空间和理解时间。


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

A.编码之先如果考虑过多的话,往往会导致程序失序;倘若考虑的可能性会全部出现的话,倒也无妨,可是如果考虑的可能性未必会出现或出现的概率很小的话,不如实际一点,让程序真正的发挥自己的价值;

B.如果抽象类过于抽象,又无实际意义的话,索性不如取消;参数过多,又无实用的话,直接remove掉会好很多;

评:与其空谈未来,不如着眼当下,设计与重构协和推进软件的质量和开发进度。


14.Temporary Filed(令人迷惑的暂时字段)

A.有时你会看到这种情况:其内某个实例变量仅为某种特定情况而设。这样的代码令人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初设置目的,会让你发疯的。

B.将这些变量提炼到一个类中,然后把所有和这个变量相关的代码都放到这个类中。

评:随意的一个变量,就像生活中随手放东西一样,等你找的时候怎么也找不到;生活总是在变 ,那些随意的动作,造成的结果也总是随意的。


15.Message Chains(过度耦合的消息链)

A.如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象。。。。。。这就是消息链。实际代码中可能看到的就是一长串的getThis()或一长串临时变量。采取这种方式,意味客户代码将与查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应修改。

B.此时应该观察消息链最终得到的对象是用来干什么的,看看能否以抽象方法把使用该对象的代码提炼到一个独立函数中,再运用移动方法把这个函数推入消息链;如果这条链上的某个对象有多位客户打算航行此航线的剩余部分,就加入一个函数来做这事。

评:逻辑链太长,管理太复杂,就像生态系统一样,生态链是不稳定的,少了任意一环都有可能导致该链中所有的环节无法正常工作。


16.Middle Man(中间人)

A.面向对象的四大特性之一便是"封装——对外部世界隐藏其内部实现,有封装也就有了”委托“,但是有可能会导致过度委托。即中间关联类过多,没有实际作用;

B.需去除中间人,也就是说把在中间关联而不起不起其他任何作用的类移除,让有关系的两个类直接交互。

评:自由恋爱的时代,何须媒人从中作梗。


17.Inappropriate Intimacy(狎昵关系)

A.如果两个类的关系过于亲密,需花费太多时间判断彼此的private变量,这就是所谓的狎昵关系了;

B.类之间关系过于亲密,可以使用Move Method和Move Filed帮助二者划清界限;同时可以使用Extract Class提炼共同点;

C.继承尤易导致亲密关系,如果子类和父类关系不大的话可以采用Replace Inheritance with Delegation,让它离开继承体系;

评:无规矩不成方圆,过于耦合必被拆散;


19.Alternative Classes with Different Interfaces(异曲同工的类)

A.如果两个函数做着相同的事却有着不同的签名,请运用Rename Method根据它们的用途重新命名,但这往往不够,请反复运用Move Method将某些行为移入类,直到两者的协议一致为止。如果你必须重复而赘余地移入代码才能完成这些,或许可以考虑使用Extract Superclass为自己赎罪。

评:不懂


20.Incomplete Library Class(不完美的库类)

A.复用常被视为对象的终极目的。不过,复用的意义常常被高估——大多数对象只要够用就好。但是,许多变成技术都是建立在程序库的基础上,而程序库如果构造的不够好,很难让我们修改其中的类使其完成我们希望完成的工作。这是否意味着重构战术用不上了呢?实际上,如果你要修改类库的函数可以通过Introduce Foreign Method,如果想要添加额外行为,就得运用Introduce Location Extension

评:不懂


21.Data Class(幼稚的封装类)

A.即指拥有一些字段和用于访问这些字段的函数,也就是我们在开发过程中使用的实体类。需要着重对待这些字段的权限,做好封装;

评:自家人,自家事


22.Refused Request(被拒绝的馈赠)

A.子类应该继承父类的数据和方法,但如果它们不想或不需要继承,该怎么办呢?如果子类复用了父类的行为,却又不愿意支持父类的接口,此时应当运用Replace Inheritance with Delegation来达到目的。


23.Comments(过多的注释)

A.如果代码中有太多的注释,那就说明我们该重构代码。如果需要注释一段代码做了什么,那就需要Extract Method,如果还需要解释该方法的功能,那就Renamed Method,如果还需要说明规格需求的话,就Introduce Assertion。


努力了一天,终于把代码的坏味道总结完毕,之中掺杂着个人的一些体会,希望能给大家带来用处,也欢迎大家多多交流。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值