Есть ли жизнеспособные альтернативы шаблону GOF Singleton?

Давайте смотреть правде в глаза. Шаблон Singleton - это тема весьма противоречивый с ордами программистов на и то и другое сторонах забора. Есть те, кто считает, что синглтон - не более чем прославленная глобальная переменная, а другие придерживаются шаблона и постоянно его используют. Однако я не хочу, чтобы Синглтон Споры лежал в основе моего вопроса. Каждый может перетянуть канат и сразиться с ним и посмотреть, кто победит, обо всем, что мне небезразлично. Я пытаюсь сказать, что не верю, что существует единственно правильный ответ, и я не пытаюсь намеренно разжечь партизанские споры. Меня просто интересует синглтон-альтернативы, когда я задаю вопрос:

Есть ли у них какие-либо конкретные альтернативы шаблону GOF Singleton?

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

Какие еще у вас есть идеи?

Обновлено: Я действительно не хочу, чтобы это был очередной пост о том, «как правильно использовать синглтон». Опять же, я ищу способы избежать этого. Для развлечения, ладно? Думаю, я задаю чисто академический вопрос голосом вашего лучшего трейлера к фильму: «Что мы могли бы сделать в параллельной вселенной, где нет синглтона?»

Какие? Это ни хорошо, ни плохо, но как его заменить? Для всех, кто говорит, что это хорошо - не участвуйте. Все, кто говорит, что это плохо, докажите это, показав мне, как я могу жить без этого. Для меня это звучит аргументированно.

S.Lott 02.10.2008 16:59

@CodingWithoutComents: прочитал весь пост. Вот почему у меня появилось чувство «не отвечайте, если вам кажется, что синглтоны в порядке».

S.Lott 02.10.2008 17:48

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

CodingWithoutComments 02.10.2008 18:17

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

S.Lott 02.10.2008 22:44

Я использую синглтоны каждый день, но разве это не мешает мне думать, что, возможно, есть способ лучше? Паттерны дизайна существуют всего 14 лет. Считаю ли я их библейской истиной? Перестанем ли мы мыслить нестандартно? Разве мы не пытаемся продвигать дисциплину CS?

CodingWithoutComments 03.10.2008 00:49

Все верно. Я согласен с поиском лучшего пути. Я просто не могу понять 1-й абзац вопроса. Он задает один вопрос: как НЕ использовать синглтоны; Я считаю, что остальные материалы о Controversy сбивают с толку и не подходят.

S.Lott 03.10.2008 00:56

В ПОРЯДКЕ. Перечитав его несколько раз, теперь я понимаю, откуда вы. Сегодня вечером я определенно попытаюсь перефразировать несколько слов. И я буду честен, споры немного не в тему - я просто как бы увидел в этом привлечение внимания.

CodingWithoutComments 03.10.2008 01:08
Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Повышение качества Laravel с помощью принципов SOLID: Лучшие практики и примеры
Когда мы говорим о том, как сделать следующий шаг в качестве разработчика, мы должны понимать, что качество кода всегда является основным фокусом на...
Принципы SOLID - лучшие практики
Принципы SOLID - лучшие практики
SOLID - это аббревиатура, обозначающая пять ключевых принципов проектирования: принцип единой ответственности, принцип "открыто-закрыто", принцип...
93
7
22 111
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Вам не нужно изо всех сил стараться избегать каких-либо шаблонов. Использование выкройки - это либо дизайнерское решение, либо естественная посадка (просто становится на свои места). При разработке системы у вас есть выбор: использовать или не использовать шаблон. Однако вы не должны изо всех сил избегать всего, что в конечном итоге является выбором дизайна.

Я не избегаю паттерна Singleton. Либо это уместно, и я им пользуюсь, либо не уместно, и я им не пользуюсь. Я считаю, что это так просто.

Приемлемость (или ее отсутствие) синглтона зависит от ситуации. Это проектное решение, которое необходимо принять, и последствия этого решения должны быть поняты (и задокументированы).

Я собирался сказать то же самое, но это не совсем ответ на его вопрос :)

Swati 02.10.2008 16:41

Пожалуйста, укажите в своем ответе, когда это уместно или нет. Довольно, пожалуйста.

CodingWithoutComments 02.10.2008 16:42

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

Thomas Owens 02.10.2008 16:42

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

Thomas Owens 02.10.2008 16:44

Хм. Я не понимаю, почему это отклоняется. Я ответил на вопрос - каковы ваши методы, позволяющие избежать использования паттерна Singleton? - говоря, что у меня нет техник, потому что я не изо всех сил стараюсь избегать шаблонов. Могут ли противники уточнить свои доводы?

Thomas Owens 02.10.2008 16:54

Вероятно, это было отвергнуто, потому что нет объяснения того, когда уместно или не уместно использовать синглтон. Синглтоны позволяют легко хранить глобальные данные для приложения и обмениваться этими данными на протяжении всей программы. Многие разработчики подумали бы, что это большое преимущество синглтонов, и продолжали бы использовать их вслепую, не осознавая, что в их дизайне есть ОГРОМНЫЙ недостаток. Использование синглтонов для хранения глобальных данных быстро превращается в кошмар управления зависимостями, и приложение становится практически невозможным для написания автоматических тестов.

tjwrona1992 25.05.2018 20:35

Используйте простой объект и объект-фабрику. Фабрика отвечает за контроль экземпляра и подробностей простого объекта только с информацией о конфигурации (например, она содержит) и поведением.

Разве Factory не часто бывает синглтон? Вот как я обычно вижу реализацию паттернов Factory.

Thomas Owens 02.10.2008 16:45

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

Есть ссылки по вышеупомянутым темам?

CodingWithoutComments 02.10.2008 16:46

Эти фреймворки кажутся специфичными для java - есть ли другие варианты для конкретного языка? Или не зависящий от языка?

CodingWithoutComments 02.10.2008 18:59

для .NET: Castle Windsor castleproject.org/container/index.html Microsoft Unity msdn.microsoft.com/en-us/library/cc468366.aspx Unity на самом деле является фреймворком для внедрения зависимостей, но, вероятно, он подойдет для этой темы.

Erik van Brakel 03.10.2008 01:51

почему за это не так много голосов? IOC - идеальное решение, чтобы избежать синглтона.

Sandeep Jindal 13.06.2013 19:46

@CodingWithoutComments Я знаю, что в PHP есть сервисный контейнер Symfony. Однако у рубиновых парней есть и другие способы внедрения зависимостей. У вас есть конкретный язык, который вас интересует?

Bevelopper 30.06.2020 13:08

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

(хотя я бы сказал, что это неправильный способ использования синглтона в первую очередь)

Если ваша проблема в том, что вы хотите сохранить состояние, вам нужен класс MumbleManager. Перед тем, как вы начнете работать с системой, ваш клиент создает MumbleManager, где Mumble - это имя системы. Благодаря этому сохраняется состояние. Скорее всего, ваш MumbleManager будет содержать пакет свойств, в котором хранится ваше состояние.

Этот тип стиля выглядит очень похожим на C, а не на объектный - вы обнаружите, что все объекты, которые определяют вашу систему, будут иметь ссылку на один и тот же MumbleManager.

Не выступая за или против синглтона, я должен сказать, что это решение является антипаттерном. MumbleManager становится «классом Бога», который содержит все виды разрозненных знаний, нарушающих единую ответственность. С тем же успехом это может быть синглтон для всех приложений.

Martin Bliss 30.04.2013 17:53

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

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

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

Ответ принят как подходящий

Алекс Миллер в "Узоры, которые я ненавижу" цитирует следующее:

"Когда синглтон кажется ответом, я считаю, что часто разумнее:

  1. Создайте интерфейс и реализацию вашего синглтона по умолчанию
  2. Создайте единственный экземпляр вашей реализации по умолчанию на «вершине» вашей системы. Это может быть конфигурация Spring, или код, или определенный различными способами в зависимости от вашей системы.
  3. Передайте единственный экземпляр каждому компоненту, который в нем нуждается (внедрение зависимостей)

Я знаю, что этому почти 5 лет, но не могли бы вы рассказать об этом подробнее? Я думаю, меня учили, что правильный способ создания синглтона - это интерфейс. Мне было бы интересно, было ли то, что я делаю, «нормально», и не было ли это так ужасно, как мне сказали.

Pureferret 06.04.2013 22:16

Лично для меня гораздо более разумный способ реализовать что-то, что ведет себя как синглтон, - это использовать полностью статический класс (статические члены, статические методы, статические свойства). В большинстве случаев я реализую это таким образом (я не могу думать о каких-либо различиях в поведении с точки зрения пользователя)

Что ты имеешь в виду, как я могу этого избежать?

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

Но нет. Мне не нужно избегать одноэлементного шаблона. Этого просто не возникает.

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

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

И да, я имел в виду слово «полиция», а не «избегать». Синглтона не следует избегать (точно так же, как нельзя избегать goto и глобальных переменных). Вместо этого вам следует контролировать его использование и убедиться, что это лучший способ эффективно выполнить то, что вы хотите.

Я прекрасно понимаю, о чем вы говорите, дружище. И я полностью согласен. Однако ваш ответ по-прежнему не отвечает конкретно на мой вопрос. Я не хотел, чтобы это был еще один вопрос «когда уместно использовать синглтон?» Меня просто интересовали жизнеспособные альтернативы синглтону.

CodingWithoutComments 02.10.2008 17:20

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

Шаблон одиночка существует, потому что бывают ситуации, когда один объект необходим для предоставления набора услуг.

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

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

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

вместо единого глобального

*pseudocode* singletonType.Instance

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

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

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

Я не совсем следую вашему примеру с обслуживаемыми контейнерами. У вас есть онлайн-ресурсы, на которые вы можете ссылаться?

CodingWithoutComments 02.10.2008 17:17

Взгляните на "Проект замка - Виндзорский контейнер" castleproject.org/container/index.html. К сожалению, очень сложно найти абстрактные публикации на эту тему.

Pop Catalin 02.10.2008 17:36

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

Я, хотя это было бы сложно управлять, но после прочтения этого сообщения в блоге "Куда делись все синглтоны?" это кажется таким естественным. Кроме того, это очень помогает изолировать ваши модульные тесты.

Итак, что вам нужно сделать? Всякий раз, когда объект зависит от другого, он получит его экземпляр только через свой конструктор (в вашем классе нет нового ключевого слова).

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 как статический метод.

Julien Grenier 23.10.2008 05:26

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

Bob Somers 07.07.2009 07:35

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

frast 12.07.2011 15:10

Это лучшее решение, которое я когда-либо видел для проблемы, которая неправильно решается с помощью синглтонов - добавление зависимостей без загромождения каждого вызова метода (и, следовательно, перепроектирование интерфейса и рефакторинг его зависимостей). Небольшая модификация идеально подходит для моей ситуации, когда не важно, чтобы был только один экземпляр «синглтона», но важно, чтобы все использовали один и тот же экземпляр по умолчанию (модификация: вместо использования фабрики используйте статический методы для внешней установки общего экземпляра и использования этого экземпляра во время построения).

meustrus 26.11.2011 18:29

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

Hari Honor 15.07.2012 18:38

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

Bevelopper 30.06.2020 13:15

Чтобы понять правильный способ обхода синглтонов, вам нужно понять, что не так с синглетонами (и глобальным состоянием в целом):

Синглтоны скрывают зависимости.

Почему это важно?

Потому что Если вы скрываете зависимости, вы теряете из виду степень связи.

Вы можете возразить, что

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 За то, чтобы обсудить корень проблемы, а не только как ее обойти.

Phil 01.04.2012 18:54

В полноценном многоуровневом приложении второй метод часто создает огромное количество ненужного кода. Загрязняя код средних уровней логикой для переноса на более низкие уровни, объекты, которые в противном случае не нужны на этом уровне, разве мы не создаем зависимости, в которых нет необходимости? Скажем, уровень 4 в стеке навигации инициирует фоновое действие, например загрузку файла. Теперь предположим, что вы хотите предупредить пользователя, когда он завершится, но к тому времени пользователь может оказаться в совершенно другой части приложения, которая имеет общий уровень 1 с начальным представлением. Тонны ненужного кода ...

Hari Honor 15.07.2012 18:32

@RasmusFaber Шаблон Singleton не скрывает зависимости. Ваша жалоба на сокрытие зависимостей - это отдельная проблема, связанная с шаблоном. Это правда, что шаблон позволяет легко совершить эту ошибку, но вполне возможно выполнить шаблон IoC и Singleton вместе в гармонии. Например, я могу создать стороннюю библиотеку, которая требует особого управления жизненным циклом ее экземпляров, и любой, кто использует мою библиотеку, должен по-прежнему «передавать» экземпляр моего синглтона своим классам, используя классическую инъекцию зависимостей, а не «небесный крючок» мой синглтон. от каждого pointcut.

Martin Bliss 30.04.2013 17:49

@HariKaramSingh, вы можете использовать шаблон подписки / публикации или общесистемную шину событий (которая должна использоваться всеми заинтересованными сторонами, а не синглом), чтобы обойти это.

Victor Sorokin 07.05.2013 20:18

@VictorSorokin, такая же проблема, если менеджер не синглтон - вы должны передать менеджера повсюду, создав тот же ненужный код, который я упоминал выше.

Hari Honor 11.05.2013 17:23

Также шаблоны pub / sub или наблюдателя могут быть столь же плохими в отношении «потери связи», как это подтвердит любой, кому приходилось выполнять отладку через фреймворк, который сильно на них полагается. По крайней мере, у синглтонов имя вызываемого метода находится в том же месте, что и вызывающий код :) Лично я думаю, что все эти стратегии имеют свое место, и ни одна из них не может быть реализована вслепую. Неряшливые кодеры превратят любой шаблон в спагетти, а ответственным кодировщикам не нужно чрезмерно обременять себя идеологическими ограничениями для создания элегантного кода.

Hari Honor 11.05.2013 17:29

«Избавьтесь от своего глобального состояния» - эээ, а что, если вам нужно глобальное состояние? Это своего рода идея синглтона.

Jez 10.03.2016 16:25

Я думаю, что этот пример можно было бы улучшить, если бы в метод передавались только значения «для текущей транзакции», а также чтобы получить ранее полученные данные creditCardProcessor и cart и сохранить их в переменных-членах.

davidfrancis 26.09.2017 17:02

Я не занимался программированием в объектно-ориентированной среде (например, 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!

mahemoff 21.04.2013 15:09

@Mark Мне кажется, что этот дизайн очень сложно заблокировать с точки зрения безопасности потоков. Должен ли я создать блокировку экземпляра для каждой статической переменной в MonoStateExample или создать одну блокировку, которую используют все свойства? Оба имеют серьезные разветвления, которые легко решаются с помощью паттерна Singleton.

Martin Bliss 30.04.2013 17:52

Этот пример является альтернативой шаблону Singleton, но не решает проблемы шаблона Singleton.

Sandeep Jindal 13.06.2013 19:27

Другие вопросы по теме