Шаблон Singleton в nodejs - нужен ли он?

Недавно я встретил эту статью о том, как написать singleton в Node.js. Я знаю документацию require утверждает, что:

Модули кэшируются после первого раза загрузки. Множественные вызовы, требуемые ('foo'), могут не вызывать код модуля несколько раз.

Таким образом, кажется, что каждый необходимый модуль может быть легко использован singleton без однострочного шаблона кода.

Вопрос:

Предоставляет ли приведенная выше статья раунд решения о создании singleton?

Ответ 1

Это в основном связано с кэшированием nodejs. Обычный и простой.

https://nodejs.org/api/modules.html#modules_caching

(v 6.3.1)

Кэширование

Модули кэшируются после первого раза загрузки. Это означает (между прочим), что каждый вызов, требующий ('foo'), получит точно тот же возвращенный объект, если он разрешит к тому же файл.

Несколько вызовов, требующих ('foo'), не могут вызывать код модуля выполняется несколько раз. Это важная функция. С этим, "частично выполненные" объекты могут быть возвращены, что позволяет зависимостей для загрузки, даже если они будут вызывать циклы.

Если вы хотите, чтобы модуль выполнял код несколько раз, затем экспортируйте функцию и вызов этой функции.

Предотвращение кэширования модуля

Модули кэшируются на основе их разрешенного имени файла. Поскольку модули могут разрешить другое имя файла в зависимости от местоположения вызывающего модуль (загрузка из папок node_modules), это не гарантия того, что require ('foo') всегда будет возвращать тот же самый объект, если он разрешить разные файлы.

Кроме того, в файловых системах или операционных системах, не учитывающих регистр, разные разрешенные имена файлов могут указывать на один и тот же файл, но кеш будут по-прежнему рассматривать их как разные модули и перезагружать файл много раз. Например, require ('./foo') и require ('./FOO') вернуть два разных объекта, независимо от того, есть или нет. /foo и. /FOO - это один и тот же файл.

Итак, простыми словами.

Если вы хотите Singleton; экспортировать объект.

Если вы не хотите Singleton; экспортировать функцию (и делать вещи/возвращать материал/что-то в этой функции).

Ответ 2

Все вышеперечисленное является сложным. Существует школа мысли, в которой говорится, что шаблоны дизайна демонстрируют недостатки реального языка.

Языки с ООП на основе прототипов (без классов) не нуждаются в одноэлементном шаблоне. Вы просто создаете один (тон) объект "на лету" и затем используете его.

Что касается модулей в node, да, по умолчанию они кэшируются, но их можно настроить, например, если вы хотите, чтобы горячая загрузка изменений модуля.

Но да, если вы хотите использовать общий объект по всему, вставляя его в экспорт модулей, все в порядке. Просто не затрудняйте его с помощью "singleton pattern", не нужно его использовать в JavaScript.

Ответ 3

Одноэлемент в node.js(или в браузере JS, если на то пошло), как это совершенно не нужно.

Поскольку модули кэшируются и сохраняются в памяти, пример, указанный в приведенной ссылке, может быть легко переписан гораздо проще:

var socketList = {};

exports.add = function (userId, socket) {
    if (!socketList[userId]) {
        socketList[userId] = socket;
    }
};

exports.remove = function (userId) {
    delete socketList[userId];
};

exports.getSocketList = function () {
    return socketList;
};
// or
// exports.socketList = socketList

Ответ 4

Посмотрите еще немного на Предостережения кэширования модуля в документах модулей:

Модули кэшируются на основе их разрешенного имени файла. Поскольку модули могут разрешаться для другого имени файла на основе местоположения вызывающего модуля (загрузка из node_modules папок), это не гарантия, которая требует ('foo'), всегда будет возвращать то же самое объект, если он будет разрешен для разных файлов.

Итак, в зависимости от того, где вы находитесь, когда вам нужен модуль, можно получить другой экземпляр модуля.

Похоже, что модули не - простое решение для создания синглетонов.

Изменить: Или, может быть, они . Как @mkoryak, я не могу придумать случай, когда один файл может разрешать разные имена файлов (без использования символических ссылок). Но (как @JohnnyHK комментирует), несколько копий файла в разных каталогах node_modules будут загружаться и сохраняться отдельно.

Ответ 5

Нет. Когда Node сбой кэширования модуля, этот одноэлементный шаблон терпит неудачу. Я изменил пример для значимого использования OSX:

var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

Это дает результат, который автор ожидал:

{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }

Но небольшая модификация поражает кеширование. В OSX сделайте следующее:

var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

Или, в Linux:

% ln singleton.js singleton2.js

Затем измените строку sg2 require:

var sg2 = require("./singleton2.js");

И бам, синглтон побежден:

{ '1': 'test' } { '2': 'test2' }

Я не знаю приемлемого способа обойти это. Если вы действительно чувствуете необходимость сделать что-то одноточечное и в порядке с загрязнением глобального пространства имен (и многих проблем, которые могут возникнуть), вы можете изменить строки автора getInstance() и exports на:

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
}

module.exports = singleton.getInstance();

Тем не менее, я никогда не сталкивался с ситуацией в производственной системе, где мне нужно было сделать что-то подобное. Я также никогда не чувствовал необходимости использовать шаблон singleton в Javascript.

Ответ 6

Вам не нужно ничего особенного, чтобы сделать singleton в js, код в статье также может быть таким:

var socketList = {};

module.exports = {
      add: function() {

      },

      ...
};

Внешний вид node.js(например, в браузере js), вам нужно добавить функцию обертки вручную (это делается автоматически в node.js):

var singleton = function() {
    var socketList = {};
    return {
        add: function() {},
        ...
    };
}();

Ответ 7

Синглтоны в JS прекрасны, им просто не нужно быть такими подробными.

В node, если вам нужен синглтон, например, чтобы использовать тот же экземпляр ORM/DB для разных файлов на уровне вашего сервера, вы можете заполнить ссылку в глобальной переменной.

Просто напишите модуль, который создает глобальный var, если он не существует, а затем возвращает ссылку на него.

@allen-luce правильно это сделал с приведенным здесь примером кода сноски:

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
};

module.exports = singleton.getInstance();

но важно отметить, что использование ключевого слова new не требуется. Любой старый объект, функция, iife и т.д. Будут работать - здесь нет вуду ООП.

если вы закрываете какой-либо объект внутри функции, которая возвращает ссылку на него, и делает эту функцию глобальной - тогда даже переназначение глобальной переменной не будет сжимать уже созданные из нее экземпляры, хотя это сомнительно полезно.

Ответ 8

Сохранение простоты.

foo.js

function foo() {

  bar: {
    doSomething: function(arg, callback) {
      return callback('Echo ' + arg);
    };
  }

  return bar;
};

module.exports = foo();

Тогда просто

var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });

Ответ 9

Единственный ответ, который использует классы ES6

// SummaryModule.js
class Summary {

  init(summary) {
    this.summary = summary
  }

  anotherMethod() {
    // do something
  }
}

module.exports = new Summary()

требуется этот синглтон с:

const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()

Проблема только в том, что вы не можете передавать параметры конструктору класса, но это можно обойти, вызвав вручную метод init.