Синглтон: как его использовать

247

Изменить:  Из другого вопроса я дал ответ, который содержит ссылки на множество вопросов/ответов о синглонах: Подробнее о синглонах здесь:

Итак, я прочитал нить Singletons: хороший дизайн или костыль?
И аргумент все еще бушует.

Я вижу синглтоны как шаблон дизайна (хороший и плохой).

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

Таким образом, основными вопросами, на которые нужно ответить, являются:

  • Когда вы должны использовать Singleton
  • Как вы правильно реализуете Singleton

Моя надежда на эту статью состоит в том, что мы можем собирать вместе в одном месте (вместо того, чтобы иметь Google и искать несколько сайтов) авторитетным источником того, когда (а затем как) правильно использовать Singleton. Также уместно будет список Анти-Использование и распространенные плохие реализации, объясняющие, почему они не работают и для хороших реализаций их слабости.


Так получилось, что мяч катится:
Я подниму руку и скажу, что это то, что я использую, но, вероятно, проблемы.
Мне нравится "Скотт Майерс", обрабатывающий предмет в своих книгах "Эффективный С++"

Хорошие ситуации для использования синглтонов (не так много):

  • Структуры ведения журнала
  • Пулы для регенерации потоков.
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

OK. Давайте рассмотрим некоторые критические замечания и другие реализации.
: -)

  • 34
    Что если позже вы решите, что хотите использовать несколько регистраторов? Или несколько потоков? Если вам нужен только один регистратор, создайте только один экземпляр и сделайте его глобальным. Синглтоны хороши только в том случае, если вам абсолютно НУЖНО быть только когда-либо, и им НУЖНО быть глобальным, ИМХО.
  • 3
    Кто сказал, что у фреймворка может быть только 1 экземпляр журнала? Один сингелтон, представляющий Framework. Framwork может затем дать вам конкретные регистраторы.
Показать ещё 3 комментария
Теги:
design-patterns
singleton

22 ответа

143
Лучший ответ

Все вы ошибаетесь. Прочтите вопрос. Ответ:

Использовать Singleton, если:

  • Если вам нужен один и только один объект типа в системе

Не используйте Singleton, если:

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

Как создать лучший синглтон:

  • Чем меньше, тем лучше. Я минималистский
  • Убедитесь, что он защищен потоками.
  • Удостоверьтесь, что он никогда не был нулевым.
  • Убедитесь, что он создан только один раз
  • Lazy или инициализация системы? До ваших требований
  • Иногда ОС или JVM создают для вас одиночные списки (например, в Java каждое определение класса является одиночным)
  • Предоставить деструктор или как-то выяснить, как распоряжаться ресурсами.
  • Используйте небольшую память
  • 3
    ссылка на википедию: en.wikipedia.org/wiki/Cargo_cult_programming
  • 14
    На самом деле, я думаю, вы тоже не совсем правы. Я бы перефразировал это так: «Если вам нужен один и только один объект типа в системе, и вам нужен глобальный доступ к нему». Акцент на потребности - мой. Не делайте это, если это удобно, только если вы ДОЛЖЕН иметь это.
Показать ещё 10 комментариев
61

Синглоты дают вам возможность сочетать две плохие черты в одном классе. Это неправильно во всех отношениях.

Синглтон дает вам:

  • Глобальный доступ к объекту и
  • Гарантирует, что можно создать не более одного объекта этого типа.

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

Номер два может звучать так, как будто это имеет смысл, но подумайте об этом. Когда вы в последний раз ** случайно * создали новый объект вместо ссылки на существующий? Поскольку это помечено С++, используйте пример с этого языка. Часто ли вы случайно пишете

std::ostream os;
os << "hello world\n";

Когда вы намеревались написать

std::cout << "hello world\n";

Конечно нет. Нам не нужна защита от этой ошибки, потому что такой ошибки просто не происходит. Если это так, правильный ответ - вернуться домой и спать 12-20 часов и надеяться, что вам станет лучше.

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

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

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

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

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

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

  • 0
    Предположим, что в одном потоке вам нужен доступ к определенному объекту базы данных, который совместно используется иерархией класса ORM, классом ReportGenerator и классом DataMaintenance. Хотя вы можете передавать экземпляр базы данных в конструктор каждого объекта, это может сделать список создания экземпляров действительно длинным и довольно не поддерживаемым. Вместо этого вы можете создать объект SharedDatabase как одиночный объект и передать его таким образом.
  • 3
    или вы можете сделать его глобальным и получить только один из недостатков синглтона. С помощью синглтона вы одновременно ограничиваетесь одним экземпляром этой базы данных. Зачем это делать? Или вы можете посмотреть, почему у вас так много зависимостей, что список экземпляров становится «очень длинным». Они все необходимы? Должны ли некоторые из них быть делегированы другим компонентам? Возможно, некоторые из них могли бы быть упакованы вместе в структуру, чтобы мы могли передать их как один аргумент. Есть много решений, все они лучше, чем одиночные.
Показать ещё 26 комментариев
32

Проблема с одиночками - не их реализация. Это то, что они объединяют две разные концепции, ни одна из которых явно не желательна.

1) Синглтоны обеспечивают глобальный механизм доступа к объекту. Хотя они могут быть немного более потокобезопасными или немного более надежными на языках без четко определенного порядка инициализации, это использование по-прежнему является моральным эквивалентом глобальной переменной. Это глобальная переменная, одетая в какой-то неловкий синтаксис (foo:: get_instance() вместо g_foo, скажем), но она выполняет ту же самую цель (один объект, доступный по всей программе) и имеет те же самые недостатки.

2) Синглеты предотвращают множественные экземпляры класса. Редко, IME, эта особенность должна быть испечена в класс. Это обычно гораздо более контекстуальная вещь; многие вещи, которые считаются одними и теми же, действительно просто случаются, чтобы быть единственными. IMO - более подходящее решение - просто создать только один экземпляр - пока вы не поймете, что вам нужно больше одного экземпляра.

  • 6
    Согласовано. Согласно некоторым, в реальном мире два несправедливости могут исправить. Но в программировании смешивание двух плохих идей не приводит к хорошей.
24

Одна вещь с шаблонами: не обобщать. У них есть все случаи, когда они полезны, и когда они терпят неудачу.

Синглтон может быть неприятным, если вам нужно проверить код. Обычно вы придерживаетесь одного экземпляра класса и можете выбирать между открытием двери в конструкторе или каким-либо способом до состояния reset и т.д.

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

Он может сделать отслеживание зависимостей более сложным. Когда все зависит от вашего Синглтона, его сложнее изменить, разделить на две части и т.д. Вы, как правило, придерживаетесь этого. Это также препятствует гибкости. Изучите структуру Injection Injection, чтобы попытаться устранить эту проблему.

  • 8
    Нет, синглтон - это намного больше, чем скрытая глобальная переменная. Вот что делает его особенно плохим. Он объединяет глобальность (которая обычно плоха) с другой концепцией, которая также плоха (не позволяя программисту создавать экземпляр класса, если он решит, что ему нужен экземпляр). Они часто используются как глобальные переменные, да. А затем они перетаскивают другой неприятный побочный эффект и наносят вред кодовой базе.
  • 7
    Следует также отметить, что синглтоны не должны быть общедоступными. Синглтон вполне может быть внутренним для библиотеки и никогда не показываться пользователю. Таким образом, они не обязательно являются «глобальными» в этом смысле.
Показать ещё 8 комментариев
12

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

Java, в частности, использует синглтоны в качестве замены глобальных переменных, поскольку все должно содержаться внутри класса. Ближе всего к глобальным переменным относятся общедоступные статические переменные, которые могут использоваться как глобальные с помощью import static

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

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

Итак, когда хорошо/плохо использовать синглтон? Совсем точно, когда было бы хорошо/плохо использовать глобальную переменную.

  • 0
    Когда глобальная переменная "хорошо"? Иногда они являются лучшим решением проблемы, но они никогда не бывают «хорошими».
  • 1
    Глобальная переменная хороша, когда она используется везде, и все могут иметь к ней доступ. Реализация машины состояния Тьюринга с одним состоянием может использовать одиночный.
Показать ещё 1 комментарий
6
  • Как вы правильно реализуете Singleton

Есть одна проблема, о которой я никогда не упоминал, что-то, с чем я столкнулся на предыдущей работе. У нас были синглтоны С++, которые были разделены между DLL, и обычная механика обеспечения одного экземпляра класса просто не работает. Проблема в том, что каждая DLL получает свой собственный набор статических переменных вместе с EXE. Если ваша функция get_instance является встроенной или частью статической библиотеки, каждая DLL завершит свою собственную копию "singleton".

Решение состоит в том, чтобы удостовериться, что одноэлементный код определен только в одной DLL или EXE, или создайте одноэлементный менеджер с этими свойствами для разбора экземпляров.

  • 10
    Да, да, я слышал, что вы любите синглтоны, поэтому я сделал синглтон для вашего синглтона, чтобы вы могли использовать анти-паттерн, а вы - анти-паттерн.
  • 0
    @ Ева, да что-то в этом роде. Я не создавал проблему, я просто должен был как-то заставить ее работать.
6

Современный дизайн С++ от Alexandrescu имеет потокобезопасный, наследуемый общий синглтон.

Для моей 2p-ценности я считаю важным иметь определенные сроки жизни для ваших синглетов (когда это абсолютно необходимо для их использования). Обычно я не допускаю, чтобы статическая функция get() создавала экземпляр чего-либо и оставляла настройку и уничтожение в какой-то выделенный раздел основного приложения. Это помогает выявить зависимости между синглтонами, но, как подчеркивалось выше, лучше всего избегать их, если это возможно.

  • 3
    Эта книга правил!
5

Первый пример не является потокобезопасным - если два потока одновременно называют getInstance, этот статический файл будет PITA. Некоторая форма мьютекса поможет.

  • 0
    Да, это отмечено в комментариях выше: * Ограничение: однопоточный дизайн * См .: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Для проблем, связанных с блокировкой в многопоточных приложениях
  • 0
    Классический синглтон с только getInstance в качестве статического метода и методов экземпляра для других операций никогда не может быть поточно-ориентированным. (хорошо, если вы не сделаете это одной на тонну потока, используя локальное хранилище потока ...)
Показать ещё 1 комментарий
4

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

Некоторые полезные аспекты синглетонов:

  • ленивый или предварительный экземпляр
  • удобно для объекта, который требует установки и/или состояния

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

Наконец, factory предоставляет технологии впрыскивания зависимостей, такие как Spring и т.д.

3

Поскольку singleton позволяет только создать один экземпляр, он эффективно контролирует репликацию экземпляра. например, вам не понадобятся несколько экземпляров lookup - например, карта поиска morse, поэтому ее упаковка в одноэлементном классе является apt. И только потому, что у вас есть один экземпляр класса, это не значит, что вы также ограничены количеством ссылок на этот экземпляр. Вы можете отправлять вызовы в очередь (чтобы избежать проблем с потоками) в случае необходимости изменений в экземпляре и эффекте. Да, общая форма синглтона является общедоступной, вы можете, конечно, изменить дизайн, чтобы создать более ограниченный доступ к Singleton. Я не устал от этого раньше, но я уверен, что это возможно. И всем тем, кто прокомментировал синглтонную картину, совершенно злой, вы должны это знать: да, это зло, если вы не используете его должным образом или внутри него, ограничиваясь эффективной функциональностью и предсказуемым поведением: не ОБЩАЯ.

3

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

3

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

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

  • 0
    Prototype Pattern также делает это, и он более гибкий. Вы также можете использовать его, когда ваш клиент сделает много экземпляров вашего дорогого класса, но только ограниченное число из них на самом деле имеет другое состояние. Например, тетронимос в тетрисе.
3

Реальное падение Синглтонов состоит в том, что они нарушают наследование. Вы не можете получить новый класс, чтобы предоставить вам расширенную функциональность, если у вас нет доступа к коду, на который ссылается Singleton. Таким образом, помимо того, что Синглтон сделает ваш код плотно связанным (исправляется с помощью шаблона стратегии... aka Dependency Injection), он также не позволит вам закрыть разделы кода из ревизии (разделяемые библиотеки).

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

  • 0
    Сами лесорубы не должны быть одиночками. Общая «широковещательная» система обмена сообщениями должна быть. Сами регистраторы являются подписчиками широковещательных сообщений.
  • 0
    Пулы потоков также не должны быть синглетонами. Общий вопрос: хотите ли вы когда-нибудь больше одного из них? Да. Когда я в последний раз использовал их, у нас было 3 разных пула потоков в одном приложении.
2

Но когда мне нужно что-то вроде Singleton, я часто получаю Schwarz Counter для его создания.

1

Я использую Singletons в качестве теста на собеседование.

Когда я прошу разработчика называть некоторые шаблоны проектирования, если все они могут назвать Singleton, они не наняты.

  • 45
    Жесткие и быстрые правила найма заставят вас упустить широкий круг потенциальных сотрудников.
  • 13
    Существует большое разнообразие идиотов. Это не значит, что их следует рассматривать для найма. Если кто-то может вообще не упомянуть ни одного шаблона проектирования, я думаю, что он предпочтительнее того, кто знает синглтон, и никаких других шаблонов.
Показать ещё 13 комментариев
0

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

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

0

Я все еще не понимаю, почему одноэлемент должен быть глобальным.

Я собирался создать singleton, где я спрятал базу данных внутри класса как приватную константную статическую переменную и создавал функции класса, которые используют базу данных, никогда не выставляя базу данных пользователю.

Я не понимаю, почему эта функция была бы плохой.

  • 0
    Я не понимаю, почему вы думаете, что это должно быть глобальным.
  • 0
    согласно этой теме, все говорили, что синглтон должен быть глобальным
Показать ещё 6 комментариев
0

Другая реализация

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};
  • 3
    Это действительно ужасно. Смотрите: stackoverflow.com/questions/1008019/c-singleton-design-pattern/… для лучшей ленивой инициализации и, что более важно, гарантированного детерминированного уничтожения.
  • 0
    Если вы собираетесь использовать указатели, Instance() должен возвращать указатель, а не ссылку. Внутри вашего .cpp файла инициализируйте экземпляр как null: Singleton* Singleton::instance_ = nullptr; , И Instance() должен быть реализован следующим образом: if (instance_ == nullptr) instance_ = new Singleton(); return instance_; ,
0

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

Синтаксис - это реализация для доступного по всему миру объекта (GAO с этого момента), хотя не все GAO являются одиночными.

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

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

С учетом этого GOAs могут быть инициализированы следующим образом:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

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

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

0

Анти-Использование:

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

  • 0
    Понижено голосование по двум причинам: 1. Синглтон может внутренне использовать полиморфные экземпляры (например, глобальный Logger использует полиморфные стратегии нацеливания) 2. Для одноэлементного имени может быть typedef, поэтому код фактически зависит от typedef.
  • 0
    В итоге я построил свою версию синглтона, которую можно расширять, используя любопытно повторяющийся шаблон.
0

Я думаю, что это самая надежная версия для С#:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Вот оптимизированная .NET версия:

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Этот шаблон можно найти в dotfactory.com.

  • 3
    Вы могли бы вырезать части, которые не имеют отношения к Singletons, чтобы облегчить чтение кода.
  • 0
    Кроме того, ваша первая версия не является поточно-ориентированной из-за возможного переупорядочения чтения / записи. См. Stackoverflow.com/questions/9666/…
Показать ещё 2 комментария
-2

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

Изменить - конечно, они должны быть доступны только для чтения!

  • 0
    Но это напрашивается на вопрос; почему язык пользователя и путь к файлу справки есть способ быть случаи вообще?
  • 2
    У нас есть глобалы для этого. Там нет необходимости делать их одиночками
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню