83 подписчика
Шаблоны проектирования: Синглотон
Начнем с чего-то наиболее простого, но наиболее известного. Синглотон, это объект, который создается ровно в одном экземпляре и за этим не надо следить, он сам за этим следит за счет того как его реализуют.
public class Singleton {
// The volatile keyword ensures that multiple threads handle the uniqueInstance variable correctly when it is being initialized to the Singleton instance.
private static volatile Singleton uniqueInstance;
// private constructor so no one can instantiate the class from outside
private Singleton() {}
// Method to return an instance of the class
public static Singleton getInstance() {
// First check without locking to improve performance
if (uniqueInstance == null) {
// Locking the class object to only let one thread in to check and instantiate the singleton
synchronized (Singleton.class) {
// Double-check whether the instance is null or not to ensure that no two instances are created
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
// other useful methods here
}
Эта идея кажется привлекательной только для тех, кто никогда с ними в коде не сталкивался. В реальности же, постоянно выясняется, что ситуаций где нужны разные объекты гораздо больше чем кажется. Сегодня была одна база данных, завтра мы подключимся к двум. Кроме того, в тестах почти всегда нужна возможность задавать любые объекты с нуля индивидуально, так как разные тесты как раз тестируют разные кейсы.
Фактически, синглотон больше используют там, где нужен доступ к объекту из совершенно разных мест приложения и не хочется его везде прокидывать. То есть с его помощью реализуют глобальную переменную, к которой есть доступ отовсюду. Подобный код говорит о двух вещах: в коде появляется много неявных зависимостей, которые особенно больно стреляют если объект имеет состояние и оно может меняться. В таком случае вы никогда не можете быть ни в чем уверенными, а в работающей программе будут появляться глюки. Ну и банально это говорит о том что абстракции текут по полной программе, раз мы работаем не через интерфейсы (не как конструкции, а по смыслу), а тянем такой объект напрямую во внутрь на любом уровне.
Но как это ни странно, все же есть разные экосистемы с разными порядками. Например эталонный пример где синглотоны никак не нужны и не используются почти никогда это spring (boot), так как там крутой контейнер зависимостей, который решает проблему доступа и управляет жизненным циклам объектов. В JavaScript, Python и Ruby подобные синглотоны встречаются, но в другой реализации. В JavaScript и Python иногда делают файлы, в которых формируются данные/объект, который может быть импортирован куда-то и этот объект инициализирован ровно один раз, так как сам файл интерпретируется только один раз. В Ruby такое принято и в самой Rails и в ее библиотеках, когда класс содержит статическое поле с объектом. Да это сильно упрощает написание кода (так как нет контейнера) и помогает новичкам, но иногда больно стреляет и если не следить за абстракциями, делает из кода кашу. Кстати поэтому в тестах на Ruby часто встречается такое когда синглотон меняют в начале теста и потом возвращают обратно в конце. Подобные хаки для синголотон встречаются и в других языках, я точно такое делал сам на php
Общий вывод такой. Синглотон лучше не использовать если есть возможность этого не делать, но в вашей экосистеме могут быть устоявшиеся каноны, которые лучше не нарушать или как минимум вы должны понимать последствия своих действий.
Про альтернативу синголотоном можно прочитать тут: https://en.wikipedia.org/wiki/Dependency_injection
p.s. Вы сталкивались с проблемами в использовании синглотонов?
3 минуты
28 февраля 2024