重构读书笔记十一——处理继承关系

重构 Jan 13, 2021

概述

继承是众所周知的面向对象特性。继承机制非常有用,却也常常被误用。本章介绍继承的使用和相关的重构方法。

11.1 函数上移

描述

把子类相同功能的函数,上移到父类中。

动机

避免重复代码是很重要的。重复的两个函数或许能正常工作,但是很容易成为滋生bug的温床。因为要经常面临修改其中一个而未修改另一个的风险。

11.2 字段上移

描述

把子类的字段挪到父类中去。

动机

和函数上移一般会配套使用,当发现子类具有类似的行为函数的时候将他们移到父类中,对应的字段也要做上移操作。另外如果发现子类有重复的字段,可以直接将他们挪到父类中。

11.3 构造函数本体上移

描述

将构造行为中公共的部分挪到父类中去。

动机

子类的构造行为中存在重复的部分,这时候可以通过挪到父类中来减少重复代码。通过super关键字来调用父类的构造方法。

11.4 函数下移

描述

将父类方法挪到子类中

动机

如果父类中的抽象函数只有某些子类实现,那么就把函数挪到子类中去。或者调整继承关系,抽象出新的父类,让相关的子类继承。

11.5 字段下移

描述

和字面意思一样,如果某个字段只有某个子类使用,就放到子类里面。

动机

一开始就不应该放到父类里

11.6 以子类取代类型码

描述

用多态子类来实现不同的业务逻辑,而不是用类型码的条件逻辑判断。

动机

和用工厂取代条件分支类似

11.7 移除子类

描述

去除没用的子类,改为用字段代替

动机

一个子类如果没有新的字段和行为的话,没有存在的意义。比如:创建了男人和女人的子类,但是男人和女人并没有独特的字段和行为。那么在父类中添加性别字段就能满足需求。

11.8 提炼超类

好好设计个超类比较重要,略

11.9 折叠继承体系

重构完,发现有些类,没有自己独立的字段和函数了,删掉它

11.10 用委托取代子类

描述

将子类中复杂的变化用委托取代
[me]:这里用委托,其实大家更经常听到的应该是依赖注入,依赖注入要比委托更清晰一些。

动机

如果一个对象的行为有明显的类别之分,继承是很自然的表达方式。我们可以把公用的数据和行为放在超类中,每个子类根据需要重写部分特性。但继承也有短板。继承不灵活,只能打一次,继承只能处理一个方向的变化。当两个变化组合的时候就很难处理。更大的问题是继承给了类之间紧密的关系,在超类上做任何更改都会影响所有子类。([me]:所以对继承体系,我一般不会超过三层,并且要基本不会变化的业务逻辑)。有一条流行的原则:“组合优于继承”。很多人把这句话解读为继承有害([me]:继承滥用真的是头大,要是用我就遵守我上一条规则)。
[me]:在spring体系中大家是习惯于用依赖注入来组合对象的。另外我的设计方案是继承体系是一个树的话,我会在树上划分切面,每个切面就是业务关注点,在切面用依赖注入实现对象组合。树就像一个根要非常稳定才行。

11.11 用委托取代超类

描述

去掉继承关系,而是持有一个超类。将字段和行为委托给超类。

动机

这就是典型的is-a问题。很多以后我们只是想复用一下字段和函数就使用了继承,但是子类本就不是父类的一个子集,全盘继承导致破坏了封装,抽象,同时领域的定义也被破坏。java里面stack继承list就是一个典型。如果只是想使用超类的某些功能,将超类注入(委托)就可以了。