«Это» - все о сфере видимости. Каждая функция имеет свою собственную область действия, и поскольку все в JS является объектом, даже функция может хранить некоторые значения в себе, используя «это». ООП 101 учит, что «это» применимо только к экземплярам объекта. Поэтому каждый раз, когда выполняет функция, новый «экземпляр» этой функции имеет новое значение «this».
Большинство людей путаются, когда пытаются использовать «это» внутри анонимных функций закрытия например:
(function(value) { this.value = value; $('.some-elements').each(function(elt){ elt.innerHTML = this.value; // uh oh!! possibly undefined }); })(2);
Итак, внутри each (), «this» не содержит «значение», которое вы ожидаете от него (от
this.value = value;над ним). Итак, чтобы справиться с этой проблемой (без каламбуры), разработчик мог:
(function(value) { var self = this; // small change self.value = value; $('.some-elements').each(function(elt){ elt.innerHTML = self.value; // phew!! == 2 }); })(2);
попробовать; вам понравится эта схема программирования
Я очень четко изложу свою точку зрения: синглтон с параметрами не является singleton .
Singleton по определению - это объект, для которого нужно создать экземпляр не более одного раза. Если вы пытаетесь передать параметры конструктору, в чем смысл синглтона?
У вас есть два варианта. Если вы хотите, чтобы ваш синглтон был инициализирован некоторыми данными, вы можете загрузить его данными после создания экземпляра , например:
SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data
Если операция, которую выполняет ваш синглтон, является повторяющейся и каждый раз с разными параметрами, с таким же успехом вы можете передать параметры выполняемому основному методу:
SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution
В любом случае создание экземпляра всегда будет без параметров.
Синглтоны обычно считаются антишаблонами и не должны использоваться. Они затрудняют тестирование кода.
Одноэлементный объект с аргументом в любом случае не имеет смысла - что бы произошло, если бы вы написали:
Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException
Ваш одноэлементный объект также не является потокобезопасным , поскольку несколько потоков могут выполнять одновременные вызовы getInstance
, в результате чего создается более одного экземпляра (возможно, с разными значениями x
).
Синглтон, конечно, является «анти-шаблоном» (при условии определения статики с переменным состоянием).
Если вам нужен фиксированный набор объектов неизменяемых значений, тогда перечисления путь к успеху. Для большого, возможно, открытого набора значений вы можете использовать репозиторий в той или иной форме - обычно на основе реализации Map
. Конечно, когда вы имеете дело со статикой, будьте осторожны с потоками (либо синхронизируйте достаточно широко, либо используйте ConcurrentMap
, либо проверяя, что другой поток не побил вас, либо используйте некоторую форму фьючерсов).
Причина, по которой вы не можете понять, как выполнить то, что вы пытаетесь сделать, вероятно, состоит в том, что то, что вы пытаетесь сделать, на самом деле не имеет смысла. Вы хотите вызвать getInstance (x)
с разными аргументами, но всегда возвращать один и тот же объект? Какое поведение вы хотите, когда вы вызываете getInstance (2)
, а затем getInstance (5)
?
Если вы хотите, чтобы тот же объект, но его внутреннее значение было другим, это единственный способ, когда он остается синглтоном, тогда вам вообще не нужно заботиться о конструкторе; вы просто устанавливаете значение в getInstance ()
на выходе объекта. Конечно, вы понимаете, что все ваши другие ссылки на синглтон теперь имеют другое внутреннее значение.
Используйте методы получения и установки, чтобы установить переменную и сделать конструктор по умолчанию закрытым. Затем используйте:
Singleton.getInstance().setX(value);
Я думаю, вам понадобится что-то вроде factory , чтобы создавать и повторно использовать объекты с различными параметрами. Это может быть реализовано с помощью синхронизированного HashMap
или ConcurrentHashMap
, отображающего параметр (например, Integer
) в параметризуемый класс «singleton».
Хотя вы можете дойти до точки, когда вместо этого вам следует использовать обычные, не одноэлементные классы (например, требующие 10.000 синглтонов с разными параметрами).
Вот пример такого хранилища:
public final class UsefulObjFactory {
private static Map<Integer, UsefulObj> store =
new HashMap<Integer, UsefulObj>();
public static final class UsefulObj {
private UsefulObj(int parameter) {
// init
}
public void someUsefulMethod() {
// some useful operation
}
}
public static UsefulObj get(int parameter) {
synchronized (store) {
UsefulObj result = store.get(parameter);
if (result == null) {
result = new UsefulObj(parameter);
store.put(parameter, result);
}
return result;
}
}
}
Чтобы продвинуть его еще дальше , Java enum
может также рассматриваться (или использоваться как) параметризованные синглтоны, хотя допускает только статические варианты с фиксированным числом.
Однако, если вам нужно распределенное решение 1 , рассмотрите какое-нибудь решение для бокового кеширования.
В вашем примере вы не используете синглтон. Обратите внимание, что если вы сделаете следующее (при условии, что Singleton.getInstance был на самом деле статическим):
Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);
Тогда значение obj2.x равно 3, а не 4. Если вам нужно сделать это, сделайте его простым классом. Если количество значений небольшое и фиксированное, вы можете рассмотреть возможность использования перечисления enum
. Если у вас возникла проблема с чрезмерной генерацией объектов (что обычно не так), вы можете рассмотреть возможность кеширования значений (и проверить источники или получить помощь с этим, поскольку очевидно, как создавать кеши без опасности утечки памяти).
Вы также можете прочитать эту статью , так как синглтоны очень легко злоупотреблять.
Модификация шаблона Singleton, использующая идиому Билла Пью об инициализации по запросу. Это потокобезопасно без накладных расходов на специализированные языковые конструкции (т.е. изменчивые или синхронизированные):
public final class RInterfaceHL {
/**
* Private constructor prevents instantiation from other classes.
*/
private RInterfaceHL() { }
/**
* R REPL (read-evaluate-parse loop) handler.
*/
private static RMainLoopCallbacks rloopHandler = null;
/**
* SingletonHolder is loaded, and the static initializer executed,
* on the first execution of Singleton.getInstance() or the first
* access to SingletonHolder.INSTANCE, not before.
*/
private static final class SingletonHolder {
/**
* Singleton instance, with static initializer.
*/
private static final RInterfaceHL INSTANCE = initRInterfaceHL();
/**
* Initialize RInterfaceHL singleton instance using rLoopHandler from
* outer class.
*
* @return RInterfaceHL instance
*/
private static RInterfaceHL initRInterfaceHL() {
try {
return new RInterfaceHL(rloopHandler);
} catch (REngineException e) {
// a static initializer cannot throw exceptions
// but it can throw an ExceptionInInitializerError
throw new ExceptionInInitializerError(e);
}
}
/**
* Prevent instantiation.
*/
private SingletonHolder() {
}
/**
* Get singleton RInterfaceHL.
*
* @return RInterfaceHL singleton.
*/
public static RInterfaceHL getInstance() {
return SingletonHolder.INSTANCE;
}
}
/**
* Return the singleton instance of RInterfaceHL. Only the first call to
* this will establish the rloopHandler.
*
* @param rloopHandler
* R REPL handler supplied by client.
* @return RInterfaceHL singleton instance
* @throws REngineException
* if REngine cannot be created
*/
public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
throws REngineException {
RInterfaceHL.rloopHandler = rloopHandler;
RInterfaceHL instance = null;
try {
instance = SingletonHolder.getInstance();
} catch (ExceptionInInitializerError e) {
// rethrow exception that occurred in the initializer
// so our caller can deal with it
Throwable exceptionInInit = e.getCause();
throw new REngineException(null, exceptionInInit.getMessage());
}
return instance;
}
/**
* org.rosuda.REngine.REngine high level R interface.
*/
private REngine rosudaEngine = null;
/**
* Construct new RInterfaceHL. Only ever gets called once by
* {@link SingletonHolder.initRInterfaceHL}.
*
* @param rloopHandler
* R REPL handler supplied by client.
* @throws REngineException
* if R cannot be loaded.
*/
private RInterfaceHL(RMainLoopCallbacks rloopHandler)
throws REngineException {
// tell Rengine code not to die if it can't
// load the JRI native DLLs. This allows
// us to catch the UnsatisfiedLinkError
// ourselves
System.setProperty("jri.ignore.ule", "yes");
rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
}
}