Давайте смотреть правде в глаза. Шаблон Singleton - это тема весьма противоречивый с ордами программистов на и то и другое сторонах забора. Есть те, кто считает, что синглтон - не более чем прославленная глобальная переменная, а другие придерживаются шаблона и постоянно его используют. Однако я не хочу, чтобы Синглтон Споры лежал в основе моего вопроса. Каждый может перетянуть канат и сразиться с ним и посмотреть, кто победит, обо всем, что мне небезразлично. Я пытаюсь сказать, что не верю, что существует единственно правильный ответ, и я не пытаюсь намеренно разжечь партизанские споры. Меня просто интересует синглтон-альтернативы, когда я задаю вопрос:
Есть ли у них какие-либо конкретные альтернативы шаблону GOF Singleton?
Например, много раз, когда я использовал шаблон singleton в прошлом, меня просто интересовало сохранение состояния / значений одной или нескольких переменных. Однако состояние / значения переменных могут сохраняться между каждым экземпляром класса с использованием статические переменные вместо использования одноэлементного шаблона.
Какие еще у вас есть идеи?
Обновлено: Я действительно не хочу, чтобы это был очередной пост о том, «как правильно использовать синглтон». Опять же, я ищу способы избежать этого. Для развлечения, ладно? Думаю, я задаю чисто академический вопрос голосом вашего лучшего трейлера к фильму: «Что мы могли бы сделать в параллельной вселенной, где нет синглтона?»
@CodingWithoutComents: прочитал весь пост. Вот почему у меня появилось чувство «не отвечайте, если вам кажется, что синглтоны в порядке».
Ну, если так попалось, прошу прощения. Я думал, что предпринял серьезные шаги, чтобы избежать поляризации. Я думал, что задал вопрос таким образом, что и любители, и ненавистники синглтонов могли бы уйти с этим, как программисты, у всех нас есть выбор - что их никогда не бывает только один правильный путь
Если я использую синглтоны, у меня нет возможности рассказать, как их обойти. Для меня это звучит поляризационно.
Я использую синглтоны каждый день, но разве это не мешает мне думать, что, возможно, есть способ лучше? Паттерны дизайна существуют всего 14 лет. Считаю ли я их библейской истиной? Перестанем ли мы мыслить нестандартно? Разве мы не пытаемся продвигать дисциплину CS?
Все верно. Я согласен с поиском лучшего пути. Я просто не могу понять 1-й абзац вопроса. Он задает один вопрос: как НЕ использовать синглтоны; Я считаю, что остальные материалы о Controversy сбивают с толку и не подходят.
В ПОРЯДКЕ. Перечитав его несколько раз, теперь я понимаю, откуда вы. Сегодня вечером я определенно попытаюсь перефразировать несколько слов. И я буду честен, споры немного не в тему - я просто как бы увидел в этом привлечение внимания.


Вам не нужно изо всех сил стараться избегать каких-либо шаблонов. Использование выкройки - это либо дизайнерское решение, либо естественная посадка (просто становится на свои места). При разработке системы у вас есть выбор: использовать или не использовать шаблон. Однако вы не должны изо всех сил избегать всего, что в конечном итоге является выбором дизайна.
Я не избегаю паттерна Singleton. Либо это уместно, и я им пользуюсь, либо не уместно, и я им не пользуюсь. Я считаю, что это так просто.
Приемлемость (или ее отсутствие) синглтона зависит от ситуации. Это проектное решение, которое необходимо принять, и последствия этого решения должны быть поняты (и задокументированы).
Я собирался сказать то же самое, но это не совсем ответ на его вопрос :)
Пожалуйста, укажите в своем ответе, когда это уместно или нет. Довольно, пожалуйста.
Это действительно ответ на вопрос. У меня нет методов, позволяющих избежать использования шаблона Singleton, потому что я не стараюсь изо всех сил избегать его, и я не думаю, что это должен делать любой разработчик.
Я не думаю, что вы можете делать обобщения, когда это уместно или нет. Все зависит от конкретного проекта и во многом зависит от конструкции рассматриваемой системы. Я не использую «правила большого пальца» для принятия подобных решений.
Хм. Я не понимаю, почему это отклоняется. Я ответил на вопрос - каковы ваши методы, позволяющие избежать использования паттерна Singleton? - говоря, что у меня нет техник, потому что я не изо всех сил стараюсь избегать шаблонов. Могут ли противники уточнить свои доводы?
Вероятно, это было отвергнуто, потому что нет объяснения того, когда уместно или не уместно использовать синглтон. Синглтоны позволяют легко хранить глобальные данные для приложения и обмениваться этими данными на протяжении всей программы. Многие разработчики подумали бы, что это большое преимущество синглтонов, и продолжали бы использовать их вслепую, не осознавая, что в их дизайне есть ОГРОМНЫЙ недостаток. Использование синглтонов для хранения глобальных данных быстро превращается в кошмар управления зависимостями, и приложение становится практически невозможным для написания автоматических тестов.
Используйте простой объект и объект-фабрику. Фабрика отвечает за контроль экземпляра и подробностей простого объекта только с информацией о конфигурации (например, она содержит) и поведением.
Разве Factory не часто бывает синглтон? Вот как я обычно вижу реализацию паттернов Factory.
Spring или любой другой IoC-контейнер неплохо справляется с этим. Поскольку классы создаются и управляются вне самого приложения, контейнер может создавать простые классы-одиночки и внедрять их там, где это необходимо.
Есть ссылки по вышеупомянутым темам?
Эти фреймворки кажутся специфичными для java - есть ли другие варианты для конкретного языка? Или не зависящий от языка?
для .NET: Castle Windsor castleproject.org/container/index.html Microsoft Unity msdn.microsoft.com/en-us/library/cc468366.aspx Unity на самом деле является фреймворком для внедрения зависимостей, но, вероятно, он подойдет для этой темы.
почему за это не так много голосов? IOC - идеальное решение, чтобы избежать синглтона.
@CodingWithoutComments Я знаю, что в PHP есть сервисный контейнер Symfony. Однако у рубиновых парней есть и другие способы внедрения зависимостей. У вас есть конкретный язык, который вас интересует?
Если вы используете синглтон для представления одного объекта данных, вы можете вместо этого передать объект данных в качестве параметра метода.
(хотя я бы сказал, что это неправильный способ использования синглтона в первую очередь)
Если ваша проблема в том, что вы хотите сохранить состояние, вам нужен класс MumbleManager. Перед тем, как вы начнете работать с системой, ваш клиент создает MumbleManager, где Mumble - это имя системы. Благодаря этому сохраняется состояние. Скорее всего, ваш MumbleManager будет содержать пакет свойств, в котором хранится ваше состояние.
Этот тип стиля выглядит очень похожим на C, а не на объектный - вы обнаружите, что все объекты, которые определяют вашу систему, будут иметь ссылку на один и тот же MumbleManager.
Не выступая за или против синглтона, я должен сказать, что это решение является антипаттерном. MumbleManager становится «классом Бога», который содержит все виды разрозненных знаний, нарушающих единую ответственность. С тем же успехом это может быть синглтон для всех приложений.
На самом деле, если вы разрабатываете с нуля, избегая синглтонов, вам, возможно, не придется обходиться без синглтонов, используя статические переменные. При использовании статических переменных вы также более или менее создаете синглтон, с той лишь разницей, что вы создаете разные экземпляры объекта, однако внутри все они ведут себя так, как если бы они использовали синглтон.
Можете ли вы привести подробный пример, где вы используете синглтон или где в настоящее время используется синглтон, и вы пытаетесь его избежать? Это могло бы помочь людям найти более изящное решение, как можно было бы справиться с ситуацией вообще без синглтона.
Кстати, у меня лично нет проблем с синглетонами, и я не могу понять проблемы, которые есть у других людей в отношении синглтонов. Я не вижу в них ничего плохого. То есть, если вы ими не злоупотребляете. Любой полезной техникой можно злоупотреблять, а злоупотребление приведет к отрицательным результатам. Другой метод, которым часто злоупотребляют, - это наследование. Тем не менее, никто не скажет, что наследование - это что-то плохое только потому, что некоторые люди ужасно злоупотребляют им.
Алекс Миллер в "Узоры, которые я ненавижу" цитирует следующее:
"Когда синглтон кажется ответом, я считаю, что часто разумнее:
Я знаю, что этому почти 5 лет, но не могли бы вы рассказать об этом подробнее? Я думаю, меня учили, что правильный способ создания синглтона - это интерфейс. Мне было бы интересно, было ли то, что я делаю, «нормально», и не было ли это так ужасно, как мне сказали.
Лично для меня гораздо более разумный способ реализовать что-то, что ведет себя как синглтон, - это использовать полностью статический класс (статические члены, статические методы, статические свойства). В большинстве случаев я реализую это таким образом (я не могу думать о каких-либо различиях в поведении с точки зрения пользователя)
Что ты имеешь в виду, как я могу этого избежать?
Чтобы «избежать» этого, это означает, что есть много ситуаций, с которыми я сталкиваюсь, в которых одноэлементный шаблон хорошо подходит, и, следовательно, я должен принять некоторые меры, чтобы разрядить эти ситуации.
Но нет. Мне не нужно избегать одноэлементного шаблона. Этого просто не возникает.
Я думаю, что лучшее место для контроля синглтона - это уровень дизайна класса. На этом этапе вы должны иметь возможность наметить взаимодействия между классами и увидеть, действительно ли что-то определенно требует, чтобы только 1 экземпляр этого класса когда-либо существовал в любой момент жизни приложения.
Если это так, то у вас есть синглтон. Если вы добавляете синглтоны для удобства во время кодирования, тогда вам действительно стоит пересмотреть свой дизайн, а также прекратить кодирование указанных синглтонов :)
И да, я имел в виду слово «полиция», а не «избегать». Синглтона не следует избегать (точно так же, как нельзя избегать goto и глобальных переменных). Вместо этого вам следует контролировать его использование и убедиться, что это лучший способ эффективно выполнить то, что вы хотите.
Я прекрасно понимаю, о чем вы говорите, дружище. И я полностью согласен. Однако ваш ответ по-прежнему не отвечает конкретно на мой вопрос. Я не хотел, чтобы это был еще один вопрос «когда уместно использовать синглтон?» Меня просто интересовали жизнеспособные альтернативы синглтону.
Я использую синглтон в основном как «контейнер методов», без какого-либо состояния. Если мне нужно поделиться этими методами со многими классами и я хочу избежать бремени создания экземпляров и инициализации, я создаю контекст / сеанс и инициализирую там все классы; все, что относится к сеансу, также имеет доступ к содержащемуся в нем «синглтону».
Шаблон одиночка существует, потому что бывают ситуации, когда один объект необходим для предоставления набора услуг.
Даже если это так, я все же считаю подход создания одиночных экземпляров с использованием глобальное статическое поле / свойство, представляющего экземпляр, неуместным. Это неуместно, потому что создает зависимость в коде между статическим полем и объектом, а не услугами, которые предоставляет объект.
Поэтому вместо классического одноэлементного шаблона я рекомендую использовать шаблон службы «как» с обслуживаемые контейнеры, где вместо использования одноэлементного объекта через статическое поле вы получаете ссылку на него через метод, запрашивающий тип требуемой службы.
*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.
вместо единого глобального
*pseudocode* singletonType.Instance
Таким образом, если вы хотите изменить тип объекта с одноэлементного на что-то еще, у вас будет несложно сделать это. Также в качестве дополнительного преимущества вам не нужно передавать выделение экземпляров объекта каждому методу.
Также см. Инверсия контроля, идея состоит в том, что, открывая синглтоны напрямую потребителю, вы создаете зависимость между потребителем и экземпляром объекта, а не сервисами объекта, предоставляемыми объектом.
Я считаю, что Спрятать по возможности использует одноэлементный шаблон, потому что его не всегда можно или желательно избежать.
Я не совсем следую вашему примеру с обслуживаемыми контейнерами. У вас есть онлайн-ресурсы, на которые вы можете ссылаться?
Взгляните на "Проект замка - Виндзорский контейнер" castleproject.org/container/index.html. К сожалению, очень сложно найти абстрактные публикации на эту тему.
Лучшее решение, с которым я столкнулся, - это использование фабричного шаблона для создания экземпляров ваших классов. Используя шаблон, вы можете гарантировать, что существует только один экземпляр класса, который используется совместно используемыми объектами.
Я, хотя это было бы сложно управлять, но после прочтения этого сообщения в блоге "Куда делись все синглтоны?" это кажется таким естественным. Кроме того, это очень помогает изолировать ваши модульные тесты.
Итак, что вам нужно сделать? Всякий раз, когда объект зависит от другого, он получит его экземпляр только через свой конструктор (в вашем классе нет нового ключевого слова).
class NeedyClass {
private ExSingletonClass exSingleton;
public NeedyClass(ExSingletonClass exSingleton){
this.exSingleton = exSingleton;
}
// Here goes some code that uses the exSingleton object
}
А потом фабрика.
class FactoryOfNeedy {
private ExSingletonClass exSingleton;
public FactoryOfNeedy() {
this.exSingleton = new ExSingletonClass();
}
public NeedyClass buildNeedy() {
return new NeedyClass(this.exSingleton);
}
}
Поскольку вы создадите экземпляр своей фабрики только один раз, будет единственный экземпляр exSingleton. Каждый раз, когда вы вызываете buildNeedy, новый экземпляр NeedyClass будет связан с exSingleton.
Надеюсь, это поможет. Пожалуйста, отметьте любые ошибки.
чтобы убедиться, что ваш Factory создается только после того, как вам понадобится частный конструктор и определите метод buildNeedy как статический метод.
Жюльен прав, это исправление имеет фундаментальный недостаток, заключающийся в том, что вы неявно утверждаете, что можете создать экземпляр только одной фабрики. Если вы примете необходимые меры предосторожности, чтобы обеспечить создание экземпляра только одной фабрики (как сказал Жюльен), вы получите ... синглтон! На самом деле этот подход просто добавляет ненужный уровень абстракции поверх синглтона.
Есть еще один способ иметь только один экземпляр. Вы можете создать экземпляр своей фабрики только один раз. Компилятор не требует принудительного выполнения этого требования. Это также помогает с юнит-тестами, когда вы используете хочу для другого экземпляра.
Это лучшее решение, которое я когда-либо видел для проблемы, которая неправильно решается с помощью синглтонов - добавление зависимостей без загромождения каждого вызова метода (и, следовательно, перепроектирование интерфейса и рефакторинг его зависимостей). Небольшая модификация идеально подходит для моей ситуации, когда не важно, чтобы был только один экземпляр «синглтона», но важно, чтобы все использовали один и тот же экземпляр по умолчанию (модификация: вместо использования фабрики используйте статический методы для внешней установки общего экземпляра и использования этого экземпляра во время построения).
Таким образом, в основном преимущество этого подхода заключается в том, что вам нужно только перетасовать экземпляр объекта один (фабрику), а не целую кучу объектов (для которых вы предпочитаете не использовать синглтоны)?
Кто сказал, что мы должны ограничивать многократное создание экземпляров объектов? Одним из аргументов против синглтона является то, что компетентные разработчики обычно достаточно компетентны, чтобы не создавать экземпляры класса несколько раз. При этом наличие одноэлементной фабрики небезынтересно. Я не считаю это ненужным слоем. Потому что вас интересует класс, который будет сгенерирован. Вам нужно несколько экземпляров, чтобы у вас могло быть несколько запущенных тестов, и вы не хотите, чтобы тест изменял состояние объекта, но состояние фабрики не должно изменяться от теста к другому.
Чтобы понять правильный способ обхода синглтонов, вам нужно понять, что не так с синглетонами (и глобальным состоянием в целом):
Синглтоны скрывают зависимости.
Почему это важно?
Потому что Если вы скрываете зависимости, вы теряете из виду степень связи.
Вы можете возразить, что
void purchaseLaptop(String creditCardNumber, int price){
CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
Cart.getInstance().addLaptop();
}
проще чем
void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart,
String creditCardNumber, int price){
creditCardProcessor.debit(creditCardNumber, amount);
cart.addLaptop();
}
но, по крайней мере, второй API дает понять, кто именно соавторы метода.
Таким образом, способ обхода синглтонов заключается не в использовании статических переменных или локаторов служб, а в преобразовании синглтонов-классов в экземпляры, которые создаются в той области, где они имеют смысл, и внедряются в компоненты и методы, которые в них нуждаются. Вы можете использовать IoC-framework, чтобы справиться с этим, или вы можете сделать это вручную, но важно избавиться от вашего глобального состояния и сделать зависимости и взаимодействие явными.
+1 За то, чтобы обсудить корень проблемы, а не только как ее обойти.
В полноценном многоуровневом приложении второй метод часто создает огромное количество ненужного кода. Загрязняя код средних уровней логикой для переноса на более низкие уровни, объекты, которые в противном случае не нужны на этом уровне, разве мы не создаем зависимости, в которых нет необходимости? Скажем, уровень 4 в стеке навигации инициирует фоновое действие, например загрузку файла. Теперь предположим, что вы хотите предупредить пользователя, когда он завершится, но к тому времени пользователь может оказаться в совершенно другой части приложения, которая имеет общий уровень 1 с начальным представлением. Тонны ненужного кода ...
@RasmusFaber Шаблон Singleton не скрывает зависимости. Ваша жалоба на сокрытие зависимостей - это отдельная проблема, связанная с шаблоном. Это правда, что шаблон позволяет легко совершить эту ошибку, но вполне возможно выполнить шаблон IoC и Singleton вместе в гармонии. Например, я могу создать стороннюю библиотеку, которая требует особого управления жизненным циклом ее экземпляров, и любой, кто использует мою библиотеку, должен по-прежнему «передавать» экземпляр моего синглтона своим классам, используя классическую инъекцию зависимостей, а не «небесный крючок» мой синглтон. от каждого pointcut.
@HariKaramSingh, вы можете использовать шаблон подписки / публикации или общесистемную шину событий (которая должна использоваться всеми заинтересованными сторонами, а не синглом), чтобы обойти это.
@VictorSorokin, такая же проблема, если менеджер не синглтон - вы должны передать менеджера повсюду, создав тот же ненужный код, который я упоминал выше.
Также шаблоны pub / sub или наблюдателя могут быть столь же плохими в отношении «потери связи», как это подтвердит любой, кому приходилось выполнять отладку через фреймворк, который сильно на них полагается. По крайней мере, у синглтонов имя вызываемого метода находится в том же месте, что и вызывающий код :) Лично я думаю, что все эти стратегии имеют свое место, и ни одна из них не может быть реализована вслепую. Неряшливые кодеры превратят любой шаблон в спагетти, а ответственным кодировщикам не нужно чрезмерно обременять себя идеологическими ограничениями для создания элегантного кода.
«Избавьтесь от своего глобального состояния» - эээ, а что, если вам нужно глобальное состояние? Это своего рода идея синглтона.
Я думаю, что этот пример можно было бы улучшить, если бы в метод передавались только значения «для текущей транзакции», а также чтобы получить ранее полученные данные creditCardProcessor и cart и сохранить их в переменных-членах.
Я не занимался программированием в объектно-ориентированной среде (например, Java), поэтому я не совсем разбираюсь в тонкостях обсуждения. Но я реализовал синглтон в PHP 4. Я сделал это как способ создания обработчика базы данных «черного ящика», который автоматически инициализируется и не должен передавать вызовы функций вверх и вниз в неполной и несколько сломанной структуре.
Прочитав несколько ссылок на шаблоны синглтонов, я не совсем уверен, что реализовал бы это точно так же снова. Что действительно было нужно, так это несколько объектов с общим хранилищем (например, фактический дескриптор базы данных), и это практически то, во что превратился мой вызов.
Как и большинство шаблонов и алгоритмов, использование синглтона «просто потому, что это круто» - это неправильная вещь. Мне нужен был действительно вызов «черного ящика», который оказался бы очень похожим на синглтон. И ИМО, это способ решить вопрос: не забывайте о шаблоне, но также посмотрите на его более широкий охват и на том, на каком уровне его экземпляр должен быть уникальным.
Monostate (описанный в книге Роберта Мартина «Гибкая разработка программного обеспечения») является альтернативой singleton. В этом шаблоне все данные класса статичны, но геттеры / сеттеры нестатичны.
Например:
public class MonoStateExample
{
private static int x;
public int getX()
{
return x;
}
public void setX(int xVal)
{
x = xVal;
}
}
public class MonoDriver
{
public static void main(String args[])
{
MonoStateExample m1 = new MonoStateExample();
m1.setX(10);
MonoStateExample m2 = new MonoStateExample();
if (m1.getX() == m2.getX())
{
//singleton behavior
}
}
}
Monostate имеет поведение, подобное синглтону, но делает это таким образом, что программист не обязательно осознает тот факт, что используется синглтон.
Это заслуживает большего количества голосов; Я приехал сюда из Google в поисках напоминания о MonoState!
@Mark Мне кажется, что этот дизайн очень сложно заблокировать с точки зрения безопасности потоков. Должен ли я создать блокировку экземпляра для каждой статической переменной в MonoStateExample или создать одну блокировку, которую используют все свойства? Оба имеют серьезные разветвления, которые легко решаются с помощью паттерна Singleton.
Этот пример является альтернативой шаблону Singleton, но не решает проблемы шаблона Singleton.
Какие? Это ни хорошо, ни плохо, но как его заменить? Для всех, кто говорит, что это хорошо - не участвуйте. Все, кто говорит, что это плохо, докажите это, показав мне, как я могу жить без этого. Для меня это звучит аргументированно.