曾用名:以工厂函数取代构造函数(Replace Constructor with Factory Method)
leadEngineer = new Employee(document.leadEngineer, "E");
leadEngineer = createEngineer(document.leadEngineer);动机
很多面向对象语言都有特别的构造函数,专门用于对象的初始化。需要新建一个对象时,客户端通常会调用构造函数。但与一般的函数相比,构造函数又常有一些丑陋的局限性。例如,Java 的构造函数只能返回当前所调用类的实例,也就是说,我无法根据环境或参数信息返回子类实例或代理对象;构造函数的名字是固定的,因此无法使用比默认名字更清晰的函数名;构造函数需要通过特殊的操作符来调用(在很多语言中是 new 关键字),所以在要求普通函数的场合就难以使用。
工厂函数就不受这些限制。工厂函数的实现内部可以调用构造函数,但也可以换成别的方式实现。
做法
新建一个工厂函数,让它调用现有的构造函数。
将调用构造函数的代码改为调用工厂函数。
每修改一处,就执行测试。
尽量缩小构造函数的可见范围。
范例
又是那个单调乏味的例子:员工薪资系统。我还是以 Employee 类表示“员工”。
class Employee…
constructor (name, typeCode) {
this._name = name;
this._typeCode = typeCode;
}
get name() {return this._name;}
get type() {
return Employee.legalTypeCodes[this._typeCode];
}
static get legalTypeCodes() {
return {"E": "Engineer", "M": "Manager", "S": "Salesman"};
}使用它的代码有这样的:
调用方…
candidate = new Employee(document.name, document.empType);也有这样的:
调用方…
const leadEngineer = new Employee(document.leadEngineer, "E");重构的第一步是创建工厂函数,其中把对象创建的责任直接委派给构造函数。
顶层作用域…
function createEmployee(name, typeCode) {
return new Employee(name, typeCode);
}然后找到构造函数的调用者,并逐一修改它们,令其使用工厂函数。
第一处的修改很简单。
调用方…
candidate = createEmployee(document.name, document.empType);第二处则可以这样使用工厂函数。
调用方…
const leadEngineer = createEmployee(document.leadEngineer, "E");但我不喜欢这里的类型码——以字符串字面量的形式传入类型码,一般来说都是坏味道。所以我更愿意再新建一个工厂函数,把“员工类别”的信息嵌在函数名里体现。
调用方…
const leadEngineer = createEngineer(document.leadEngineer);顶层作用域…
function createEngineer(name) {
return new Employee(name, "E");
}