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
函数并且您想要扩展它以便您还可以添加 Engineer
s.
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 的应用——不要考虑对工厂功能的更改,考虑使用它的模块。无需更改实例化员工对象的所有模块中的所有代码,您需要做的就是为他们提供一个不同的工厂。
由于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
函数并且您想要扩展它以便您还可以添加 Engineer
s.
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 的应用——不要考虑对工厂功能的更改,考虑使用它的模块。无需更改实例化员工对象的所有模块中的所有代码,您需要做的就是为他们提供一个不同的工厂。