class Party {...}
 
class Employee extends Party {
 constructor(name, id, monthlyCost) {
  super();
  this._id = id;
  this._name = name;
  this._monthlyCost = monthlyCost;
 }
}
 
 
class Party {
 constructor(name){
  this._name = name;
 }
}
 
class Employee extends Party {
 constructor(name, id, monthlyCost) {
  super(name);
  this._id = id;
  this._monthlyCost = monthlyCost;
 }
}

动机

构造函数是很奇妙的东西。它们不是普通函数,使用它们比使用普通函数受到更多的限制。

如果我看见各个子类中的函数有共同行为,我的第一个念头就是使用提炼函数(106)将它们提炼到一个独立函数中,然后使用函数上移(350)将这个函数提升至超类。但构造函数的出现打乱了我的算盘,因为它们附加了特殊的规则,对一些做法与函数的调用次序有所限制。要对付它们,我需要略微不同的做法。

如果重构过程过于复杂,我会考虑转而使用以工厂函数取代构造函数(334)。

做法

如果超类还不存在构造函数,首先为其定义一个。确保让子类调用超类的构造函数。

使用移动语句(223)将子类中构造函数中的公共语句移动到超类的构造函数调用语句之后。

逐一移除子类间的公共代码,将其提升至超类构造函数中。对于公共代码中引用到的变量,将其作为参数传递给超类的构造函数。

测试。

如果存在无法简单提升至超类的公共代码,先应用提炼函数(106),再利用函数上移(350)提升之。

范例

我以下列“雇员”的例子开始:

class Party {}
 
class Employee extends Party {
 constructor(name, id, monthlyCost) {
  super();
  this._id = id;
  this._name = name;
  this._monthlyCost = monthlyCost;
 }
 // rest of class...
class Department extends Party {
 constructor(name, staff){
  super();
  this._name = name;
  this._staff = staff;
 }
 // rest of class...

Party 的两个子类间存在公共代码,也即是对名字(name)的赋值。我先用移动语句(223)将 Employee 中的这行赋值语句移动到 super()调用后面:

class Employee extends Party {
  constructor(name, id, monthlyCost) {
    super();
    this._name = name;
    this._id = id;
    this._monthlyCost = monthlyCost;
  }
  // rest of class...

测试。之后我将这行公共代码提升至超类的构造函数中。由于其中引用了一个子类构造函数传入的参数 name,于是我将该参数一并传给超类构造函数。

class Party…

constructor(name){
  this._name = name;
}

class Employee…

constructor(name, id, monthlyCost) {
  super(name);
  this._id = id;
  this._monthlyCost = monthlyCost;
}

class Department…

constructor(name, staff){
  super(name);
  this._staff = staff;
}

运行测试。然后大功告成。

多数时候,一个构造函数的工作原理都是这样:先(通过 super 调用)初始化共用的数据,再由各个子类完成额外的工作。但是,偶尔也需要将共用行为的初始化提升至超类,这时问题便来了。

请看下面的例子。

class Employee…

constructor (name) {...}
 
get isPrivileged() {...}
 
assignCar() {...}

class Manager extends Employee…

constructor(name, grade) {
  super(name);
  this._grade = grade;
  if (this.isPrivileged) this.assignCar(); // every subclass does this
}
 
get isPrivileged() {
  return this._grade >4;
}

这里我无法简单地提升 isPrivileged 函数至超类,因为调用它之前需要先为 grade 字段赋值,而该字段只能在子类的构造函数中初始化。

在这种场景下,我可以对这部分公共代码使用提炼函数(106)。

class Manager…

constructor(name, grade) {
  super(name);
  this._grade = grade;
  this.finishConstruction();
}
 
finishConstruction() {
  if (this.isPrivileged) this.assignCar();
}

然后再使用函数上移(350)将提炼得到的函数提升至超类。

class Employee…

finishConstruction() {
  if (this.isPrivileged) this.assignCar();
}