问题描述
由于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);