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

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

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

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

Какая другая идея у вас есть?

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

Ответ 1

Алекс Миллер в разделе Шаблоны, которые я ненавижу, приводит следующее:

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

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

Ответ 2

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

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

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

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

Вы можете утверждать, что

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 дает понять, что такое коллабораторы методов.

Таким образом, способ обхода Singletons не использовать статические переменные или сервисные локаторы, а изменять классы Singleton в экземпляры, которые создаются в области, где они имеют смысл, и вставляются в компоненты и методы, которые в них нуждаются, Вы можете использовать IoC-framework для этого, или вы можете сделать это вручную, но важно избавиться от вашего глобального состояния и сделать явно зависимыми и совлокальные действия.

Ответ 3

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

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

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

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

И затем factory.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

Как только вы создадите экземпляр factory только один раз, будет один экземпляр exSingleton. Каждый раз, когда вы вызываете buildNeedy, новый экземпляр NeedyClass будет связан с exSingleton.

Надеюсь, это поможет. Пожалуйста, укажите любые ошибки.

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

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

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

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

*pseudocode* singletonType.Instance

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

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

Мое мнение заключается в спрятать использование шаблона singleton, когда это возможно, потому что это не всегда возможно избежать или желательно.

Ответ 7

Моностат (описанный в Robert C. Martin Agile Software Development) является альтернативой синглтону. В этом шаблоне данные класса все статичны, а геттеры/сеттеры не статичны.

Например:

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 имеет аналогичное поведение с singleton, но делает это таким образом, что программист не обязательно осведомлен о том, что используется один сингл.

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

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

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

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

Ответ 15

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

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

Как и большинство паттернов и алгоритмов, использование одноэлементного "только потому, что оно круто" - это "Неправильная вещь". Мне нужен был действительно "черный ящик", который оказался очень похожим на синглтон. И ИМО, чтобы решить этот вопрос: будьте в курсе шаблона, но посмотрите на него более широкий охват и на каком уровне он должен быть уникальным.

Ответ 16

Что вы имеете в виду, какие мои методы избегают?

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

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