js中的工厂模式是否违反了开闭原则?

问题描述

由于JS不支持抽象类和继承,每次使用工厂模式时我们要添加一个新的类型来创建,我们都必须修改代码,这意味着我们违反了开闭原则。例如,在下面的快照中 - 如果我们想添加一个新的员工类型,比如 Marketing,我们将不得不更新 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();
    }
}

解决方法

如果我们想添加一个新的员工类型,我们将不得不更新 switch 语句,这违反了开闭原则。

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

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

,

只需创建一个包含构造函数的 employeeType 对象:

const employeeType = {
  Accountant,Developer,Sales
}

console.log(new (employeeType["Accountant"])());

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

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

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

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

假设您可以单独访问 CreateEmployee 函数,并且想要对其进行扩展,以便还可以添加 Engineer

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 中的价值很小,因为它是无类型的。当然,函数以及构造函数都是一等公民,可以轻松传递给 new 运算符。

由于JS不支持抽象类或继承

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

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

,

如果您希望动态创建 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);