ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
曾用名:以工厂函数取代构造函数(Replace Constructor with Factory Method) ![](https://box.kancloud.cn/bec9a72f4d680612ac7bd7328d4fa66e_623x242.jpeg) `leadEngineer = new Employee(document.leadEngineer, 'E');`![](https://box.kancloud.cn/a3bed334e2e1f6d1a46c5039deb25af9_91x152.jpeg) `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'); } ```