跳转到内容

第八部分导读:处理继承关系

继承是面向对象里最有力也最容易用错的工具。这一部分的手法回答两个问题:父类和子类之间,每个字段、每个方法该放在哪一层;以及这里到底该不该用继承。

第一个问题出在成员放错了层。两个子类各抄一份同样的 annual_cost,改规则要改两处;父类上放着只有销售才有的 quota,工程师对象也带着一个永远是 0 的指标,还有人拿“指标为 0”来判断谁是工程师。前者要上移,后者要下移。第二个问题出在继承本身。用一个字符串字段区分员工种类、到处 match,是该有子类却没有;子类里只剩一个返回常量的方法,是有子类却不需要;订票创建出来就定死了是普通还是尊享、没法补差价升级,是继承太僵硬,该换成持有一个委托对象。继承在创建对象时就定下了,只能沿一个方向分类,还会把父类的全部接口一并接过来;委托可以在运行时更换,可以按几个维度各自变化,只暴露用得上的那部分。两者没有绝对的优劣:真正“是一种”的关系用继承最省事,“有一个”或“会变”的关系用委托更稳。

手法 一句话 流行度 档
函数上移 / 字段上移 / 构造函数本体上移(第 51 章) 把几个子类里相同的成员搬到父类 ★★ 完整
函数下移 / 字段下移(第 51 章) 把只有一个子类用的成员搬到那个子类 ★ 完整
以子类取代类型码(第 52 章) 用一组子类替掉表示种类的字段 ★★ 完整
移除子类(第 53 章) 只剩常量的子类换回字段 ★ 精简
提炼超类(第 54 章) 给两个做类似事情的类建共同的父类 ★★ 精简
折叠继承体系(第 55 章) 没有区别的父类和子类合并成一个 ★ 速览
以委托取代子类 / 以委托取代超类(第 56 章) 把继承改成持有一个对象 ★★ 完整

闻到什么味道先看哪一章:

  • 重复代码,几个子类有一样的方法、字段或构造函数开头 → 第 51 章 函数上移、字段上移、构造函数本体上移;两个没有共同父类的类做着类似的事 → 第 54 章 提炼超类。
  • 被拒绝的遗赠,子类继承了用不上的成员 → 第 51 章 函数下移、字段下移;父类的大部分接口对子类都没有意义 → 第 56 章 以委托取代超类。
  • 重复的 switch,同一个类型字段在多个方法里被判断 → 第 52 章 以子类取代类型码。
  • 冗赘的元素,子类只剩一个返回常量的方法 → 第 53 章 移除子类;子类已经空了 → 第 55 章 折叠继承体系。
  • 过大的类,为了同时按几个维度区分、或者要在运行时换类型而组合出一堆子类 → 第 56 章 以委托取代子类。