这是一个新的javascript工厂模式吗
is this a new javascript factory pattern?
公平的警告-很久以前,我写了很多C++,忍不住想把javascript强行引入我当时熟悉的设计模式中。在任何回复中指责我返祖是可以的;-)
在我当前的项目中,我想按名称创建对象,这表示工厂模式。因此,我阅读了谷歌点击量最高的"javascript工厂模式"页面。他们都有一个丑陋的共同点:
if (name === 'FactoryPartA') {
parentClass = PartA;
} else if (name === 'FactoryPartB') {
parentClass = PartB;
} else if ...
parentClass = PartZ;
}
return new parentClass();
它有两个问题:
- 每次我为工厂创建一个新零件时,我都必须编辑工厂的实现,我更喜欢避免这两项工作;错误插入机会
- 这是一个硬编码的线性搜索,几乎没有"效率!"
这就是我所想到的——模块和工厂模式的组合,以及通过在工厂的register
闭包中定义工厂零件类的策略提供的模块模式的信息隐藏优点。
最后谈到我的问题:我不敢相信以前没有比我更好的程序员做到过这一点,所以如果你知道的话,请分享一个链接,链接到这个工厂模式的规范版本。
注意:在这个例子中,我把所有的代码都放在一起运行。在我的项目中,工厂、FactoryPartA、FactoryPart B和客户端代码都在单独的文件中。
namespace('mynamespace');
// object factory
mynamespace.factory = (function () {
'use strict';
var api = {};
var registry = [];
// register an item
api.register = function (item) {
if (registry.some (function (r) {return r.name === item.name;})) {
throw new Error ('factory.register(): name collision detected: ' + name);
} else {
registry.push(item);
}
};
// make an item given its name
api.make = function (name) {
var item = null;
var idx = registry.findIndex (function (r) {
return r.name === name;
});
if (idx >= 0) {
item = new registry[idx].make();
}
return item;
};
return api;
})();
// define a module & register it with factory
mynamespace.factory.register ({
name: 'FactoryPartA',
make: function FactoryPartA () {
'use strict';
var label = 'Factory Part A'; // private property
this.test = undefined; // public property
this.label = function () { // public method
return label;
};
return this;
}
});
// define a different module & register it with factory
mynamespace.factory.register ({
name: 'FactoryPartB',
make: function FactoryPartB () {
'use strict';
var label = 'Factory Part B';
this.test = undefined;
this.label = function () {
return label;
};
return this;
}
});
// client code
var aPart = mynamespace.factory.make('FactoryPartA');
var bPart = mynamespace.factory.make('FactoryPartB');
console.log (aPart.label()); // logs 'Factory Part A'
console.log (bPart.label()); // logs 'Factory Part B'
var anotherPart = mynamespace.factory.make('FactoryPartA');
aPart.test = 'this one is not';
anotherPart.test = 'the same as this one';
console.log (aPart.test !== anotherPart.test); // logs true
要回答一个基本问题——这是一个新的Javascript工厂模式吗——不,不是。(查看ES6/ES2015、Typescript和Aurelia的依赖注入模块。)
现在,为了回答整个语句,您实际上正在尝试将元数据添加到Javascript中的"类"类型中。问题是,看起来你正试图创建一个由工厂组成的工厂——我不确定你是否需要。(也许你已经简化了你的例子。)
以你为例,我会做一些更像这样的事情:
namespace('mynamespace');
// object factory
mynamespace.factory = (function () {
'use strict';
var api = {};
var registry = {};
// register an item
api.register = function (name, item, overwrite) {
if (!overwrite || registry.hasOwnProperty('name')) {
throw new Error ('factory.register(): name collision detected: ' + name);
}
registry[name] = item;
};
// make an item given its name
api.make = function (name) {
var item = registry[name];
return item ? new item() : null; // or better, Object.create(item);
};
return api;
})();
// define a module & register it with factory
mynamespace.factory.register ('FactoryPartA', function FactoryPartA () {
'use strict';
var label = 'Factory Part A'; // private property
this.test = undefined; // public property
this.label = function () { // public method
return label;
};
});
// define a different module & register it with factory
mynamespace.factory.register ('FactoryPartB', function FactoryPartB () {
'use strict';
var label = 'Factory Part B';
this.test = undefined;
this.label = function () {
return label;
};
});
这将删除额外的工厂物料,使其不是FactoryFactory。(不知道你为什么会这样。)此外,你提到了线性搜索——通过使用对象而不是数组,可以避免线性查找(对象哈希是恒定时间查找)。最后,您实际上可以注册任何名称,而无需将其放入包装器对象中。这更接近DI。
不过,如果您真的想采用元数据风格的方法,您可以执行以下操作:
namespace('mynamespace');
// object factory
mynamespace.factory = (function () {
'use strict';
var api = {};
var registry = {};
// register an item
api.register = function (item, overwrite) {
if (!overwrite || registry.hasOwnProperty(item.name)) {
throw new Error ('factory.register(): name collision detected: ' + item.name);
}
registry[item.name] = item;
};
// make an item given its name
api.make = function (name) {
var item = registry[name];
return item ? new item() : null; // or better, Object.create(item);
};
return api;
})();
// define a module & register it with factory
mynamespace.factory.register (function FactoryPartA () {
'use strict';
var label = 'Factory Part A'; // private property
this.test = undefined; // public property
this.label = function () { // public method
return label;
};
});
// define a different module & register it with factory
mynamespace.factory.register (function FactoryPartB () {
'use strict';
var label = 'Factory Part B';
this.test = undefined;
this.label = function () {
return label;
};
});
这将使用函数名而不是单独的属性。(函数在Javascript中可以是命名的或匿名的。这种方法不适用于匿名函数。)我上面提到了Typescript,因为当Typescript编译到Javascript时,它实际上会在对象上放置很多自己的元数据,这与您正在做的类似。我提到Aurelia的依赖项注入,因为它的@autoinject
功能实际上读取这些元数据来创建对象,这与您正在做的类似。
但实际上。。。。除非您试图创建一个依赖注入容器(这将比您的示例中需要更多的逻辑-您希望容器中的键也可以指向实例),否则我认为Object.create()
和Object.assign()
将比使用此模式获得更多的功能。在静态类型、强类型、编译语言中所需的许多设计模式在该环境之外是不必要的。所以你可以这样做:
function PartA () {
'use strict';
var label = 'Factory Part A'; // private property
this.test = undefined; // public property
this.label = function () { // public method
return label;
}
function PartB () {
'use strict';
var label = 'Factory Part B';
this.test = undefined;
this.label = function () {
return label;
};
}
var A = Object.create(PartA);
var B = Object.create(PartB);
简单地说,这要容易得多。
- 这是一个新的javascript工厂模式吗
- Javascript-如何将类工厂定义为Extjs风格
- Javascript:什么是工厂
- JavaScript 工厂模式中的检查实例
- JavaScript 对象原型未通过工厂返回
- 用纯 javascript 调用角度工厂
- 尝试在 JavaScript 中使用工厂调用方法时未定义
- 我们如何使用新的运算符和工厂模式在JavaScript中创建类的实例
- 闭包中的Javascript实例化.如何使工厂和模型独立
- 如何在JavaScript的类工厂中设置类名
- Javascript:需要帮助创建一个类似工厂的函数
- Javascript UMD -在哪里/如何是根,工厂定义
- Angularjs / javascript如何更新工厂创建的单例
- Javascript函数工厂嵌套在一个即时函数内嵌套jQuery准备好了
- 带javascript和工厂女孩的水豚
- 工厂函数与对象.create - JavaScript -何时使用哪个
- 如何用工厂函数编写JavaScript
- 是否有一种方法可以在Angularjs/javascript中执行工厂/类的每个方法之前/之后调用函数
- Javascript对象工厂方法,将参数object作为参数传递
- JavaScript、Eval和New——穷人的工厂