通常我们可以使用Node.js创建一个简单的单例对象:
var foo = {};
module.exports = foo;
或者
function Foo(){}
module.exports = new Foo();
然而
制作需要外部变量进行初始化的干净的单例模块的最佳方法是什么?我最终做了这样的事情:
var value = null;
function init(val){
if(value === null){
value = val;
}
return value;
}
module.exports = init;
这样,使用模块的人就可以为某个变量传递一个初始化值。另一种方法是这样的:
function Baz(value){
this.value = value;
}
var instance = null;
module.exports = function init(value){
if(instance === null){
instance = new Baz(value);
}
return instance;
}
我遇到两个问题:
(1)这是次要的,但语义是错误的。我们可以将init重命名为getInstance,但是我们不能使相同的函数字面意思是“初始化并获取”,因为它们的含义不同。因此,我们必须有一个功能,可以执行两种不同的操作。创建一个实例并检索和实例。我特别不喜欢这样做,因为在某些情况下,我们需要确保用于初始化实例的参数不为null。在多个开发人员使用一个模块的情况下,尚不清楚一个模块是否已初始化,如果它们未定义地传递到尚未初始化的模块中,则可能会成为问题,或者至少会使您感到困惑。
(2)这一点更为重要-在某些情况下,初始化Baz是异步的。例如,建立Redis连接或从文件读取以初始化单例,或建立socket.io连接。这才是真正让我兴奋的东西。
例如,这是一个我认为非常丑陋的模块,它存储一个socket.io连接:
var io = null;
var init = function ($io) {
if (io === null) {
io = $io;
io.on('connection', function (socket) {
socket.on('disconnect', function () {
});
});
}
return io;
};
module.exports = {
getSocketIOConn: init
};
上面的模块初始化如下:
var server = http.createServer(app);
var io = socketio.listen(server);
require('../controllers/socketio.js').getSocketIOConn(io);
因此,我正在寻找一种设计模式,该模式允许我们创建一个初始化过程为异步的单例模块。理想情况下,初始化实例和检索实例都不会具有相同的功能。这样的事情存在吗?
我认为没有必要创建解决此问题的模式的方法,但是也许我犯了错误的结构化代码结构,即创建了一个不需要存在的问题-初始化a一个值只有一次的模块,但是使用一个函数来初始化实例和检索实例。
听起来您正在尝试创建一个在某个位置进行初始化的模块,然后将该初始化中的某些共享资源用于该模块的其他用户。在现实世界中,这是一种半普遍的需求。
首先,理想的情况是模块可以加载或创建它所依赖的东西,因为这使它自己更具模块化和实用性,并且减轻了使用该模块的人的负担。因此,在您的情况下,如果您的模块仅能创建/加载首次创建该模块时所需的东西,并将该资源存储在其自己的模块变量中,那么这将是理想的情况。但是,这并非总是可能的,因为共享资源可能是其他人设置和初始化的责任,并且仅需要使该模块意识到这一点。
因此,执行此操作的常用方法是仅对模块使用构造函数。在Javascript中,您可以允许构造函数采用一个可选参数来提供初始化信息。负责设置模块的代码将使用所需的设置参数调用构造函数。不负责模块设置的模块的其他用户可能只是不调用构造函数,或者如果他们想要返回值,或者应该传递其他构造函数参数,则可以null
为该设置参数传递。
例如,您可以这样做:
var io;
module.exports = function(setup_io) {
if (setup_io) {
io = setup_io;
}
return module.exports;
};
module.exports.method1 = function() {
if (!io) {
throw new Error("Can't use method1 until io is properly initalized");
}
// code here for method1
};
// other methods here
然后,该模块的用户可以执行以下操作:
// load myModule and initialize it with a shared variable
var myModule = require('myModule')(io);
或这个:
// load myModule without initializing it
// (assume some other module will initialize it properly)
var myModule = require('myModule');
注意:为使开发人员更加精明,有一些需要适当设置的方法(在可以正确使用之前),当调用任何需要进行设置的方法以正确通知时,检查是否已设置模块即可。开发人员,他们在正确设置模块之前已经调用了方法。否则,错误可能会在更远的下游发生,并且可能没有有用的错误消息。
如果现在希望初始化过程是异步的,那么也可以这样做,但这肯定会使模块的其他用法复杂化,因为它们不一定知道何时/是否已初始化模块。
var moduleData;
var readyList = new EventEmitter();
module.exports = function(arg, callback) {
// do some async operation here involving arg
// when that operation completes, you stored the result
// in local module data and call the callback
readyList.on("ready", callback);
someAsyncOperation(arg, function() {
// set moduleData here
// notify everyone else that the module is now ready
readyList.emit("ready");
// remove all listeners since this is a one-shot event
readyList.removeAllListeners("ready");
});
return module.exports;
};
如果您希望该模块的其他用户在初始化完成时得到通知,则可以允许他们自己注册一个回调,以便在模块准备就绪时得到通知。
// pass a callback to this method that will be called
// async when the module is ready
module.exports.ready = function(fn) {
// if module already ready, then schedule the callback immediately
if (moduleData) {
setImmediate(fn);
} else {
readyList.on("ready", fn);
}
};
如果出于某种原因(我不太了解),您想使用相同的构造函数进行初始化和就绪检测,那么可以这样做,尽管我认为它不像使用单独的方法进行就绪检测那样清晰:
var moduleData;
var readyList = new EventEmitter();
module.exports = function(arg, callback) {
// if both arguments passed, assume this is a request for module
// initialization
if (arguments.length === 2) {
// do some async operation here involving arg
// when that operation completes, you stored the result
// in local module data and call the callback
readyList.on("ready", callback);
someAsyncOperation(arg, function() {
// set moduleData here
// notify everyone else that the module is now ready
readyList.emit("ready");
// remove all listeners since this is a one-shot event
readyList.removeAllListeners("ready");
});
} else {
// constructor called just for a ready request
// arg is the callback
if (moduleData) {
// if module already ready, then schedule the callback immediately
setImmediate(arg);
} else {
// otherwise, save the callback
readyList.on("ready", arg);
}
}
return module.exports;
};
异步初始化模块的用法:
// async initialization form
var myModule = require("myModule")(someArg, function() {
// can use myModule here
});
加载模块并在其他人初始化它时得到通知的用法:
var myModule = require("myModule")(function() {
// can use myModule here
});
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句