Niedawno natknąłem się na ten artykuł o tym, jak napisać singleton w Node.js. Znam dokumentację require
stanów, które:
Moduły są buforowane po pierwszym załadowaniu. Wielokrotne wywołania
require('foo')
mogą nie powodować wielokrotnego wykonywania kodu modułu.
Wygląda więc na to, że każdy wymagany moduł może być łatwo użyty jako singleton bez pojedynczego kodu kotłowego.
Pytanie:
Czy powyższy artykuł zawiera opis rozwiązania tworzenia singletona?
javascript
node.js
design-patterns
singleton
mkoryak
źródło
źródło
Odpowiedzi:
Ma to zasadniczo związek z buforowaniem nodejs. Jasne i proste.
https://nodejs.org/api/modules.html#modules_caching
(wersja 6.3.1)
Więc w prostych słowach.
Jeśli chcesz Singleton; wyeksportować obiekt .
Jeśli nie chcesz Singletona; eksportuj funkcję (i wykonaj rzeczy / zwrócenie rzeczy / cokolwiek w tej funkcji).
Aby być BARDZO jasnym, jeśli zrobisz to poprawnie, powinno działać, spójrz na https://stackoverflow.com/a/33746703/1137669 (odpowiedź Allena Luce'a). Wyjaśnia w kodzie, co się dzieje, gdy buforowanie nie powiedzie się z powodu inaczej rozwiązanych nazw plików. Ale jeśli ZAWSZE wybierasz tę samą nazwę pliku, to powinno działać.
Aktualizacja 2016
tworzenie prawdziwego singletona w node.js z symbolami es6 Inne rozwiązanie : w tym linku
Zaktualizuj 2020
Ta odpowiedź odnosi się do CommonJS (własnego sposobu importowania / eksportowania modułów przez Node.js). Node.js najprawdopodobniej przejdzie na moduły ECMAScript : https://nodejs.org/api/esm.html (ECMAScript to prawdziwa nazwa JavaScript, jeśli nie wiesz)
Podczas migracji do ECMAScript przeczytaj na razie następujące informacje: https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards
źródło
Wszystkie powyższe są zbyt skomplikowane. Istnieje szkoła myślenia, która mówi, że wzorce projektowe wykazują braki w prawdziwym języku.
Języki z OOP opartym na prototypach (bezklasowych) w ogóle nie potrzebują pojedynczego wzorca. Po prostu tworzysz pojedynczy (tonowy) obiekt w locie, a następnie go używasz.
Jeśli chodzi o moduły w węźle, tak, domyślnie są one buforowane, ale można je dostosować, na przykład, jeśli chcesz ładować zmiany modułów na gorąco.
Ale tak, jeśli chcesz używać wspólnego obiektu na całym świecie, umieszczenie go w eksporcie modułu jest w porządku. Po prostu nie komplikuj tego za pomocą „pojedynczego wzorca”, nie ma takiej potrzeby w JavaScript.
źródło
There is a school of thought which says design patterns are showing deficiencies of actual language.
Nie. Gdy buforowanie modułu Node nie powiedzie się, pojedynczy wzorzec nie powiedzie się. Zmodyfikowałem przykład, aby działał sensownie na 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());
Daje to wynik, jakiego przewidywał autor:
{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }
Ale mała modyfikacja pokonuje buforowanie. W systemie OSX zrób to:
var sg = require("./singleton.js"); var sg2 = require("./SINGLETON.js"); sg.add(1, "test"); sg2.add(2, "test2"); console.log(sg.getSocketList(), sg2.getSocketList());
Lub w systemie Linux:
Następnie zmień
sg2
wymaganą linię na:var sg2 = require("./singleton2.js");
I bam , singleton został pokonany:
{ '1': 'test' } { '2': 'test2' }
Nie znam akceptowalnego sposobu obejścia tego. Jeśli naprawdę czujesz potrzebę stworzenia czegoś podobnego do singletona i nie przeszkadza ci zanieczyszczanie globalnej przestrzeni nazw (i wiele problemów, które mogą z tego wyniknąć), możesz zmienić autora
getInstance()
iexports
wiersze na:singleton.getInstance = function(){ if(global.singleton_instance === undefined) global.singleton_instance = new singleton(); return global.singleton_instance; } module.exports = singleton.getInstance();
To powiedziawszy, nigdy nie spotkałem się z sytuacją w systemie produkcyjnym, w której musiałbym zrobić coś takiego. Nigdy też nie czułem potrzeby używania wzorca singleton w Javascript.
źródło
Patrząc nieco dalej na zastrzeżenia dotyczące buforowania modułów w dokumentacji modułów:
Tak więc, w zależności od tego, gdzie jesteś, gdy potrzebujesz modułu, można uzyskać inną instancję modułu.
Wygląda na to, że moduły nie są prostym rozwiązaniem do tworzenia singletonów.
Edycja: A może są . Podobnie jak @mkoryak, nie mogę wymyślić przypadku, w którym pojedynczy plik mógłby zostać rozwiązany na różne nazwy plików (bez użycia dowiązań symbolicznych). Ale (jak komentarze @JohnnyHK), wiele kopii pliku w różnych
node_modules
katalogach będzie ładowanych i przechowywanych oddzielnie.źródło
node_modules
których każdy zależy od tego samego modułu, ale istnieją oddzielne kopie tego zależnego modułu wnode_modules
podkatalogu każdego z dwóch różnych modułów.require('./db')
jest w dwóch oddzielnych plikach, koddb
modułu jest wykonywany dwukrotnierequire('../lib/myModule.js');
jeden plik, arequire('../lib/mymodule.js');
drugi i nie dostarczyłem tego samego obiektu.Singleton w node.js (lub w przeglądarce JS, jeśli o to chodzi), jest zupełnie niepotrzebny.
Ponieważ moduły są buforowane i stanowe, przykład podany w linku, który podałeś, można łatwo przepisać znacznie prościej:
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
źródło
may not
zastosowanie, gdynpm link
podczas opracowywania innych modułów. Dlatego zachowaj ostrożność podczas korzystania z modułów, które opierają się na pojedynczej instancji, takiej jak eventBus.Nie potrzebujesz nic specjalnego do zrobienia singletona w js, równie dobrze kod w artykule mógłby wyglądać następująco:
var socketList = {}; module.exports = { add: function() { }, ... };
Poza node.js (na przykład w przeglądarce js), musisz ręcznie dodać funkcję wrapper (jest to robione automatycznie w node.js):
var singleton = function() { var socketList = {}; return { add: function() {}, ... }; }();
źródło
Jedyna odpowiedź, która używa klas ES6
// SummaryModule.js class Summary { init(summary) { this.summary = summary } anotherMethod() { // do something } } module.exports = new Summary()
wymagaj tego singletona z:
const summary = require('./SummaryModule') summary.init(true) summary.anotherMethod()
Jedynym problemem jest to, że nie można przekazać parametrów do konstruktora klasy, ale można to obejść, ręcznie wywołując
init
metodę.źródło
summary
w innej klasie bez jej ponownego inicjowania?Singletony są w porządku w JS, po prostu nie muszą być tak gadatliwe.
W węźle, jeśli potrzebujesz singletona, na przykład, aby użyć tej samej instancji ORM / DB w różnych plikach w warstwie serwera, możesz umieścić odwołanie w zmiennej globalnej.
Po prostu napisz moduł, który tworzy globalną zmienną, jeśli nie istnieje, a następnie zwraca do niej odniesienie.
@ allen-luce miał rację z przykładowym kodem przypisu skopiowanym tutaj:
singleton.getInstance = function(){ if(global.singleton_instance === undefined) global.singleton_instance = new singleton(); return global.singleton_instance; }; module.exports = singleton.getInstance();
ale ważne jest, aby pamiętać, że użycie
new
słowa kluczowego nie jest wymagane. Każdy stary obiekt, funkcja, życie itp. Będzie działać - nie dzieje się tutaj żadne OOP voodoo.punkty bonusowe, jeśli zamkniesz jakiś obiekt wewnątrz funkcji, która zwraca do niej odniesienie i sprawisz, że ta funkcja będzie globalna - wtedy nawet ponowne przypisanie zmiennej globalnej nie zbije instancji już utworzonych z niej - chociaż jest to wątpliwie przydatne.
źródło
module.exports = new Foo()
ponieważ module.exports nie uruchomi się ponownie, chyba że zrobisz coś naprawdę głupiegoUpraszczanie.
foo.js
function foo() { bar: { doSomething: function(arg, callback) { return callback('Echo ' + arg); }; } return bar; }; module.exports = foo();
Wtedy po prostu
var foo = require(__dirname + 'foo'); foo.doSomething('Hello', function(result){ console.log(result); });
źródło