js中的工厂模式是否违反了Open-Close原则?

Does the Factory Pattern in js violate the Open-Close principle?

由于JS不支持抽象类,不支持继承,所以在使用工厂模式的时候,每次要添加一个新的类型来创建,都需要修改代码,违反了open-关闭原则。例如,在下面的快照中——如果我们想添加一个新的员工类型,比如市场营销,我们将不得不更新 switch 语句,这违反了开闭原则。是否有任何解决方法可以在不违反开闭原则的情况下使用工厂模式?

function Accountant(){
    console.log('I am accountant');
}

function Developer(){
    console.log('I am developer');
}

function Sales(){
    console.log('I am sales');
}

function CreateEmployee(employee){
    switch(employee){
        case('accountant'): return new Accountant();
        case('developer'): return new Developer()
        case('sales'): return new Sales();
    }
}

如果您希望动态创建 new 个实例,您应该使用对象字面量。查看以下设计,了解您可能想要或不想要如何解决这个问题:

function ucFirst(string){
  const s = string.split('');
  return s.shift().toUpperCase()+s.join('');
}
function InstanceController(){
  this.instances = [];
  this.add = (name, obj)=>{
    this.instances.push({name:name, obj:obj});
    return this;
  }
  this.get = name=>{
    for(let o of this.instances){
      if(o.name === name){
        return o.obj;
      }
    }
    return false;
  }
  this.remove = name=>{
    for(let i=0,a=this.instances,l=a.length; i<l; i++){
      if(a[i].name === name){
         a.splice(i, 1);
        break;
      }
    }
    return this;
  }
}
const ic = new InstanceController;
const data1 = {
  data:'could be from database', 
  more:'sure there can be more data',
  numberTest: 2
}
const data2 = {test:'just a test'};
ic.add('developer', data1).add('accountant', {testing:'see'});
let dev = ic.get('developer'), aco = ic.get('accountant');
if(dev)console.log(dev);
if(aco)console.log(aco);
console.log(ic.get('nope'));
ic.remove('accountant'); aco = ic.get('accountant');
console.log(aco);

朝着正确方向迈出的一步是创建一个包含构造函数的 employeeType 对象:

const employeeType = {
  Accountant,
  Developer,
  Salesperson
};

// console.log(new (employeeType["Accountant"])());
// Abstract away the hard-coded type above; the below randomization is strictly for demo purpose

const employeeTypeKeys = Object.keys(employeeType);
const employeeTypeIndex = Math.floor(employeeTypeKeys.length * Math.random());
console.log(new (employeeType[employeeTypeKeys[employeeTypeIndex]])());

function Accountant(){
    console.log('I am an accountant');
}

function Developer(){
    console.log('I am a developer');
}

function Salesperson(){
    console.log('I am a salesperson');
}

我认为这种模式不完全是我想要扩展的东西,但它是可能的。

假设您可以单独访问 CreateEmployee 函数并且您想要扩展它以便您还可以添加 Engineers.

import CreateEmployee from "./employee.js";

function Engineer(){
   console.log("I'm an Engineer");
}

function CreateEmployeeAndEngineer(employeeType){
    if(employeeType === 'Engineer') return new Engineer();
    else {
        return CreateEmployee(employeeType);
    }
}

简单的(呃...不是真的)函数组合。

但是,在 Javascript 中它的价值很小,因为它是无类型的。然后当然是函数,因此构造函数是第一个 class 公民,可以很容易地传递给 new 运算符。

since JS does not support abstract classes or inheritance

Javascript 通过它的原型链概念支持继承。

如果需要,您可以实施 Factory method 模式。

if we want to add a new employee type, we will have to update the switch statement which is violation of the open-close principle.

不,不是。 OCP 并不是要禁止更新代码。如果我们要实现一个新的功能,当然需要去接触程序的代码。 OCP 是关于设计您的接口,以便您的程序可以轻松扩展而无需到处更改代码 - 理想情况下,您只需提供新代码,并更改程序的配置使用它 - 或者如果这是不可配置的,则仅更改高级构建块。

我认为工厂模式甚至可以促进 OCP 的应用——不要考虑对工厂功能的更改,考虑使用它的模块。无需更改实例化员工对象的所有模块中的所有代码,您需要做的就是为他们提供一个不同的工厂