Я читал разные мнения о единственном образце. Некоторые утверждают, что этого следует избегать любой ценой, а другие могут быть полезны в определенных ситуациях.
Одна из ситуаций, в которой я использую синглеты, - это когда мне нужна фабрика (скажем, объект f типа F) для создания объектов определенного класса А. Фабрика создается один раз с использованием некоторых параметров конфигурации, а затем используется каждый раз, когда объект тип A создается. Таким образом, каждая часть кода, которая хочет создать экземпляр A, извлекает синглтон f и создает новый экземпляр, например
F& f = F::instance();
boost::shared_ptr<A> a = f.createA();
Так что общий мой сценарий таков, что
- Мне нужен только один экземпляр класса либо по причинам оптимизации (мне не нужны несколько объектов фабрики), либо для совместного использования общего состояния (например, фабрика знает, сколько экземпляров A она все еще может создать)
- Мне нужен способ получить доступ к этому экземпляру f из F в разных местах кода.
Я не заинтересован в обсуждении, является ли этот шаблон хорошим или плохим, но если предположить, что я хочу избежать использования синглтона, какой другой шаблон я могу использовать?
У меня были идеи (1) получить фабричный объект из реестра или (2) создать фабрику в какой-то момент во время запуска программы, а затем передать фабрику как параметр.
В решении (1) сам реестр является одноэлементным, поэтому я только что перенес проблему неиспользования одного экземпляра с завода на реестр.
В случае (2) мне нужен некоторый исходный источник (объект), из которого происходит фабричный объект, поэтому я боюсь, что я снова вернусь к другому синглтону (объекту, который предоставляет мой экземпляр фабрики). Следуя этой цепочке синглетонов, я могу свести проблему к одному синглтону (целому приложению), с помощью которого все другие синглтоны управляются прямо или косвенно.
Будет ли этот последний вариант (с использованием одного исходного синглтона, который создает все другие уникальные объекты и внедряет все другие синглтоны в нужных местах) приемлемым решением? Является ли это решением, которое неявно предлагается, когда кто-то советует не использовать синглтоны, или каковы другие решения, например, в примере, показанном выше?
РЕДАКТИРОВАТЬ
Поскольку я думаю, что суть моего вопроса была неправильно понята некоторыми, вот еще немного информации. Как объяснено, например, здесь , слово singleton может указывать (а) на класс с одним экземпляром объекта и (б) шаблон проектирования, используемый для создания и доступа к такому объекту.
Чтобы прояснить ситуацию, давайте используем термин уникальный объект для (а) и шаблон синглтона для (б). Итак, я знаю, что такое одноэлементный шаблон и внедрение зависимостей (кстати, в последнее время я интенсивно использую DI для удаления экземпляров одноэлементного шаблона из некоторого кода, над которым я работаю).
Моя точка зрения заключается в том, что, если весь объектный граф не будет создан из одного объекта, находящегося в стеке основного метода, всегда будет необходимость доступа к некоторым уникальным объектам через шаблон синглтона.
Мой вопрос заключается в том, является ли единственное решение, не связанное с одноэлементным шаблоном, от того, зависит ли полное создание графов объектов и связывание от основного метода (например, с помощью некоторой мощной структуры DI, которая не использует сам шаблон) .
Ответы:
Ваш второй вариант - отличный способ - это своего рода внедрение зависимостей, которое является шаблоном, используемым для совместного использования состояния в вашей программе, когда вы хотите избежать синглетонов и глобальных переменных.
Вы не можете обойти тот факт, что что- то должно создать вашу фабрику. Если это что-то происходит с приложением, пусть будет так. Важным моментом является то, что вашей фабрике должно быть все равно, какой объект ее создал, а объекты, получающие фабрику, не должны зависеть от того, откуда она родом. Не заставляйте ваши объекты получать указатель на синглтон приложения и запрашивать его для фабрики; пусть ваше приложение создаст фабрику и передаст ее тем объектам, которые в ней нуждаются.
источник
Цель синглтона состоит в том, чтобы обеспечить существование только одного экземпляра в определенной области. Это означает, что синглтон полезен, если у вас есть веские причины для принудительного поведения синглтона; на практике это случается редко, а многопроцессорность и многопоточность, безусловно, стирают значение слова «уникальный» - это один экземпляр на машину, на процесс, на поток, на запрос? А ваша одиночная реализация заботится об условиях гонки?
Вместо синглтона я предпочитаю использовать одно из:
Причина в том, что синглтон является скрытым глобальным состоянием, а это означает, что он обеспечивает высокую степень связи во всем приложении - любая часть вашего кода может захватывать экземпляр синглтона из любого места с минимальными усилиями. Если вы используете локальные объекты или пропускаете экземпляры, у вас гораздо больше контроля, и вы можете держать свои области небольшого размера и узкие зависимости.
источник
Большинство людей (в том числе и вы) совершенно не понимают, что собой представляет паттерн Синглтон. Паттерн Singleton означает только то, что существует один экземпляр класса, и существует некоторый механизм для кода во всем приложении, чтобы получить ссылку на этот экземпляр.
В книге GoF метод статической фабрики, который возвращает ссылку на статическое поле, был лишь примером того, как может выглядеть этот механизм, и с серьезными недостатками. К сожалению, все и их собака зацепились за этот механизм и подумали, что это и есть синглтон.
Альтернативы, которые вы цитируете, на самом деле тоже синглтоны, просто с другим механизмом получения ссылки. (2) явно приводит к слишком большому объему передачи, если только вам не нужна ссылка только в нескольких местах рядом с корнем стека вызовов. (1) звучит как грубая структура внедрения зависимостей - так почему бы не использовать ее?
Преимущество состоит в том, что существующие структуры DI являются гибкими, мощными и хорошо протестированными. Они могут сделать гораздо больше, чем просто управлять синглетонами. Однако, чтобы в полной мере использовать их возможности, они лучше всего работают, если вы структурируете свое приложение определенным образом, что не всегда возможно: в идеале, существуют центральные объекты, которые получены через инфраструктуру DI и все их зависимости транзитивно заполняются и затем выполнен.
Изменить: В конечном итоге все зависит от основного метода в любом случае. Но вы правы: единственный способ полностью избежать использования global / static - это настроить все из основного метода. Обратите внимание, что DI наиболее популярен в серверных средах, где основной метод непрозрачен и устанавливает сервер, а код приложения состоит в основном из обратных вызовов, которые создаются и вызываются кодом сервера. Но большинство DI-структур также предоставляют прямой доступ к своему центральному реестру, например SpringCon ApplicationContext, для «особых случаев».
Так что в основном лучшее, что люди придумали, - это умная комбинация двух альтернатив, которые вы упомянули.
источник
Есть несколько альтернатив:
Внедрение зависимости
Каждый объект имеет свои зависимости переданы ему, когда он был создан. Обычно за создание и связывание объектов отвечают либо каркас, либо небольшое количество фабричных классов.
Сервисный реестр
Каждому объекту передается объект реестра службы. Он предоставляет методы для запроса различных объектов, которые предоставляют разные услуги. Платформа Android использует этот шаблон
Root Manager
Существует один объект, который является корнем графа объекта. По ссылкам из этого объекта любой другой объект может быть в конечном итоге найден. Код имеет тенденцию выглядеть так:
источник
Singleton::instance
всюду в клиентском коде.Если ваши потребности от синглтона можно свести к одной функции, почему бы просто не использовать простую заводскую функцию? Глобальные функции (возможно, статический метод класса F в вашем примере) по своей природе являются одиночными, а уникальность обеспечивается компилятором и компоновщиком.
Следует признать, что это нарушается, когда работа синглтона не может быть сведена к одному вызову функции, хотя, возможно, небольшой набор связанных функций может помочь вам.
Из всего сказанного, пункты 1 и 2 в вашем вопросе дают понять, что вы действительно просто хотите чего-то. В зависимости от вашего определения одноэлементного паттерна, вы уже используете его или очень близки. Я не думаю, что вы можете иметь что-то, не будучи синглтоном или, по крайней мере, очень близким к одному. Это просто слишком близко к значению синглтона.
Как вы предлагаете, в какой-то момент у вас должно быть что-то одно, поэтому, возможно, проблема не в том, чтобы иметь какой-то один экземпляр, а в том, чтобы предпринять шаги, чтобы предотвратить (или, по крайней мере, препятствовать или минимизировать) злоупотребление этим. Хорошим началом является перемещение как можно большего количества состояний из «синглтона» в параметры. Сужение интерфейса к синглтону также помогает, так как вероятность злоупотреблений таким образом меньше. Иногда вам просто нужно смириться и сделать синглтон очень надежным, как куча или файловая система.
источник
Если вы используете мультипарадигмальный язык, такой как C ++ или Python, одна альтернатива одноэлементному классу - это набор функций / переменных, заключенных в пространство имен.
Концептуально говоря, файл C ++ со свободными глобальными переменными, свободными глобальными функциями и статическими переменными, используемыми для сокрытия информации, все они заключены в пространство имен, дает вам почти тот же эффект, что и одноэлементный «класс».
Это сломается только если вы хотите наследования. Я видел много синглетонов, которые были бы лучше в этом смысле.
источник
Инкапсуляция синглетонов
Кейс-сценарий. Ваше приложение является объектно-ориентированным и требует 3 или 4 отдельных синглетонов, даже если у вас больше классов.
Перед примером (C ++, как псевдокод):
Один из способов заключается в частичном удалении некоторых (глобальных) синглетонов путем их инкапсуляции в уникальный синглтон, в состав которого входят другие синглтоны.
Таким образом, вместо «нескольких синглетонов - подход, а не синглтон вообще, подход», мы имеем «инкапсулировать все синглеты в один подход».
После примера (C ++, как псевдокод):
Обратите внимание, что примеры больше похожи на псевдокод, игнорируют незначительные ошибки или синтаксические ошибки и рассматривают решение вопроса.
Есть и другая вещь, которую следует учитывать, потому что используемый язык программирования может влиять на то, как реализовать вашу одноэлементную или не одноэлементную реализацию.
Приветствия.
источник
Ваши оригинальные требования:
Не совпадайте с определением синглтона (и того, к чему вы обращаетесь позже). С GoF (моя версия 1995) стр. 127:
Если вам нужен только один экземпляр, это не мешает вам делать больше.
Если вам нужен один глобально доступный экземпляр, создайте один глобально доступный экземпляр . Нет необходимости иметь шаблон имени для всего, что вы делаете. И да, отдельные глобально доступные экземпляры обычно плохие. Если дать им имя, это не сделает их менее плохими .
Чтобы избежать глобальной доступности, общий подход «создай экземпляр и передай его» или «владелец двух объектов склеит их вместе» работает хорошо.
источник
Как насчет использования контейнера IoC? С некоторыми мыслями вы можете получить что-то вроде:
Вы должны будете указать, какую реализацию
IFoo
использовать при запуске, но это единственная конфигурация, которую вы можете легко заменить позже. Время жизни разрешенного экземпляра обычно может контролироваться контейнером IoC.Статический метод синглтоновых пустот можно заменить на:
Метод статического одноэлементного геттера можно заменить на:
Пример относится к .NET, но я уверен, что очень похожее может быть достигнуто на других языках.
источник