Что такого плохого в синглетах?
Паттерн синглтона является полностью оплаченным членом книги паттернов GoF, но в последнее время он кажется довольно осиротевшим в мире разработчиков. Я до сих пор использую довольно много синглетов, особенно для фабричных классов, и, хотя вы должны быть немного осторожны с проблемами многопоточности (как и любой другой класс на самом деле), я не понимаю, почему они такие ужасные.
Особенно похоже, что переполнение стека предполагает, что все согласны с тем, что синглтоны - это зло. Зачем?
Пожалуйста, поддержите ваши ответы "фактами, ссылками или конкретными знаниями"
36 ответов
Перефразированный от Брайана Баттона:
Они обычно используются в качестве глобального экземпляра, почему это так плохо? Потому что вы скрываете зависимости вашего приложения в своем коде, вместо того, чтобы показывать их через интерфейсы. Создание чего-то глобального, чтобы избежать его распространения, является запахом кода.
Они нарушают принцип единственной ответственности: в силу того, что они контролируют свое собственное творение и жизненный цикл.
Они по своей сути заставляют код быть тесно связанным. Это во многих случаях затрудняет их тестирование.
Они несут состояние в течение всей жизни приложения. Еще один удар по тестированию, так как вы можете столкнуться с ситуацией, когда нужно заказывать тесты, что является большим нет для тестов юнитов. Зачем? Потому что каждый модульный тест должен быть независимым от другого.
Синглтоны решают одну (и только одну) проблему.
Ресурс Спор.
Если у вас есть какой-то ресурс, который
(1) может иметь только один экземпляр, и
(2) вам нужно управлять этим единственным экземпляром,
тебе нужен синглтон
Там не так много примеров. Файл журнала большой. Вы не хотите просто отказаться от одного файла журнала. Вы хотите очистить, синхронизировать и закрыть его правильно. Это пример единого общего ресурса, которым нужно управлять.
Это редко, что вам нужен синглтон. Причина, по которой они плохие, заключается в том, что они чувствуют себя глобальными и являются полностью оплаченным членом книги " Шаблоны дизайна GoF ".
Когда вы думаете, что вам нужен глобальный, вы, вероятно, делаете ужасную ошибку дизайна.
Некоторые снобы кодирования смотрят на них свысока как на прославленную глобализацию. Так же, как многие люди ненавидят утверждение goto, так и другие ненавидят идею когда-либо использовать глобальный. Я видел, как несколько разработчиков пошли на экстраординарные меры, чтобы избежать глобального, потому что они рассматривали возможность использования одного из них как допущение неудачи. Удивительно, но факт.
На практике паттерн Singleton - это просто метод программирования, который является полезной частью вашего набора концепций. Время от времени вы можете найти это идеальное решение, и поэтому используйте его. Но использовать его просто так, чтобы вы могли похвастаться использованием шаблона проектирования, так же глупо, как отказываться от его использования, потому что это всего лишь глобальный характер.
Миско Хевери, из Google, имеет несколько интересных статей именно на эту тему...
Синглтоны являются патологическими У лжецов есть пример модульного тестирования, который иллюстрирует, как синглтоны могут усложнить определение цепочек зависимостей и запуск или тестирование приложения. Это довольно экстремальный пример злоупотребления, но мысль, которую он высказывает, остается в силе:
Синглтоны - не что иное, как глобальное государство. Благодаря глобальному состоянию ваши объекты могут тайно овладевать вещами, которые не объявлены в их API, и, как следствие, Singletons превращают ваши API в патологических лжецов.
Там, где все унесенные Singletons указывают на то, что внедрение зависимостей облегчает получение экземпляров для конструкторов, которые в них нуждаются, что устраняет основную потребность в плохих, глобальных Singletons, о которых говорилось в первой статье.
Я думаю, что путаница вызвана тем фактом, что люди не знают реального применения паттерна Синглтона. Я не могу подчеркнуть это достаточно. Синглтон - это не шаблон для переноса глобалов. Шаблон Singleton должен использоваться только для гарантии того, что один и только один экземпляр данного класса существует во время выполнения.
Люди думают, что Синглтон - зло, потому что они используют его для глобалов. Именно из-за этой путаницы на Синглтона смотрят свысока. Пожалуйста, не путайте синглтоны и глобалы. Если вы используете его для той цели, для которой он предназначен, вы получите исключительные преимущества от паттерна Singleton.
В синглетах очень плохо то, что вы не можете легко их расширять. По сути, вам нужно встроить какой-нибудь шаблон декоратора или что-то подобное, если вы хотите изменить их поведение. Кроме того, если в один прекрасный день вы захотите иметь несколько способов сделать это, это может быть довольно болезненным, в зависимости от того, как вы выложили свой код.
Стоит отметить, что если вы действительно используете синглтоны, попробуйте передать их тому, кто в них нуждается, а не иметь прямой доступ к ним... В противном случае, если вы когда-либо решите использовать несколько способов выполнения того, что делает синглтон, это будет довольно трудно изменить, поскольку каждый класс встраивает зависимость, если он напрямую обращается к синглтону.
Итак, в основном:
public MyConstructor(Singleton singleton) {
this.singleton = singleton;
}
скорее, чем:
public MyConstructor() {
this.singleton = Singleton.getInstance();
}
Я полагаю, что этот тип паттерна называется внедрением зависимостей и обычно считается хорошей вещью.
Как и в случае с любым другим шаблоном... Подумайте об этом и подумайте, является ли его использование в данной ситуации неуместным или нет... Правила обычно нарушаются, и шаблоны не должны применяться бездумно, не задумываясь.
Паттерн синглтона сам по себе не является проблемой. Проблема заключается в том, что шаблон часто используется людьми, разрабатывающими программное обеспечение с объектно-ориентированными инструментами, не имея четкого понимания концепций ОО. Когда синглтоны вводятся в этом контексте, они имеют тенденцию превращаться в неуправляемые классы, которые содержат вспомогательные методы для каждого небольшого использования.
Синглтоны также являются проблемой с точки зрения тестирования. Они, как правило, затрудняют написание отдельных юнит-тестов. Инверсия управления (IoC) и внедрение зависимостей - это паттерны, предназначенные для решения этой проблемы объектно-ориентированным способом, который поддается модульному тестированию.
В среде сборки мусора синглтоны могут быстро стать проблемой в отношении управления памятью.
Существует также многопоточный сценарий, когда синглтоны могут стать узким местом, а также проблемой синхронизации.
Синглтон реализуется с использованием статического метода. Люди, которые проводят модульное тестирование, избегают статических методов, потому что их нельзя высмеять или зарезать. Большинство людей на этом сайте являются большими сторонниками юнит-тестирования. Общепринятым соглашением избегать их является использование инверсии схемы управления.
Синглтоны также плохи, когда дело доходит до кластеризации. Потому что тогда у вас больше нет "ровно одного синглтона" в вашем приложении.
Рассмотрим следующую ситуацию: Как разработчик, вы должны создать веб-приложение, которое обращается к базе данных. Чтобы гарантировать, что одновременные вызовы базы данных не конфликтуют друг с другом, вы создаете сохранение потока SingletonDao
:
public class SingletonDao {
// songleton's static variable and getInstance() method etc. omitted
public void writeXYZ(...){
synchronized(...){
// some database writing operations...
}
}
}
Таким образом, вы уверены, что в вашем приложении существует только один синглтон, и все базы данных проходят через этот единственный SingletonDao
, Ваша производственная среда теперь выглядит так:
Пока все хорошо.
Теперь предположим, что вы хотите настроить несколько экземпляров своего веб-приложения в кластере. Теперь у вас вдруг что-то вроде этого:
Это звучит странно, но теперь у вас есть много синглетонов в вашем приложении. И это именно то, чем не должен быть синглтон: иметь много объектов. Это особенно плохо, если вы, как показано в этом примере, хотите выполнять синхронизированные вызовы в базу данных.
Конечно, это пример плохого использования синглтона. Но смысл этого примера таков: вы не можете полагаться на то, что в вашем приложении есть ровно один экземпляр синглтона, особенно когда речь идет о кластеризации.
- Он легко (ab) используется как глобальная переменная.
- Классы, которые зависят от синглетонов, относительно сложны для модульного тестирования в изоляции.
Синглтон не о единственном экземпляре!
В отличие от других ответов, я не хочу говорить о том, что не так с Singletons, но чтобы показать вам, насколько они мощные и потрясающие при правильном использовании!
- Проблема: Singleton может быть проблемой в многопоточной среде
Решение: Используйте однопоточный процесс начальной загрузки для инициализации всех зависимостей вашего синглтона. - Проблема: трудно издеваться над одиночками.
Решение: использовать метод Factory pattern для издевательстваMyModel myModel = Factory.inject(MyModel.class);
Вы можете сопоставитьMyModel
вTestMyModel
класс, который наследует его везде, гдеMyModel
будет введен, вы получитеTestMyModel
instread. - Проблема: синглтоны могут стать причиной появления лука-порея, поскольку они никогда не утилизируются.
Решение: Ну, избавьтесь от них! Внедрите в свое приложение функцию обратного вызова для правильной утилизации синглетонов, вы должны удалить все связанные с ними данные и, наконец, удалить их из Factory.
Как я уже говорил, в названии синглтона речь идет не об одном экземпляре.
- Singletons улучшает читабельность: вы можете посмотреть на ваш класс и посмотреть, какой синглтон он внедрил, чтобы выяснить, каковы его зависимости.
- Singletons улучшает обслуживание: после того, как вы удалили зависимость из класса, вы только что удалили однокомпонентную инъекцию, вам не нужно переходить и редактировать большую ссылку других классов, которые просто перемещали вашу зависимость (это вонючий код для меня @Jim Burger)
- Singletons улучшает память и производительность: когда что-то происходит в вашем приложении, и для доставки требуется длинная цепочка обратных вызовов, вы тратите впустую память и производительность, с помощью Singleton вы сокращаете средний уровень и повышаете производительность и использование памяти (избегая ненужных локальных переменных размещения).
Монополия - это дьявол, а синглтоны с нечитаемым / изменяемым состоянием - это "настоящая" проблема...
После прочтения " Синглетоны - это патологические лжецы", как это было предложено в ответе Джейсона, я наткнулся на этот маленький кусочек, который дает лучший представленный пример того, как синглеты часто используются неправильно.
Глобальный это плохо, потому что:
- а. Это вызывает конфликт пространства имен
- б. Это подвергает государство необоснованному
Когда дело доходит до синглетонов
- а. Явный ОО способ их вызова предотвращает конфликты, поэтому укажите а. это не проблема
- б. Синглтоны без состояния (как и фабрики) не являются проблемой. Синглтоны с состоянием могут снова попасть в две категории, которые являются неизменяемыми или пишутся один раз и читают много (файлы конфигурации / свойств). Это не плохо. Mutable Singletons, которые являются своего рода держателями ссылок, - это те, о которых вы говорите.
В последнем утверждении он ссылается на концепцию блога "одиночки - лжецы".
Как это относится к монополии?
Чтобы начать игру в монополию, сначала:
- сначала мы устанавливаем правила, чтобы все были на одной странице
- в начале игры всем дают равный старт
- только один набор правил представлен, чтобы избежать путаницы
- правила не могут меняться на протяжении всей игры
Теперь для тех, кто на самом деле не играл монополию, эти стандарты в лучшем случае идеальны. Трудно проглотить поражение в монополии, потому что монополия - это деньги, если вы проигрываете, вы должны тщательно следить за тем, как остальные игроки заканчивают игру, а потери обычно бывают быстрыми и сокрушительными. Таким образом, правила обычно изменяются в какой-то момент, чтобы служить личным интересам некоторых игроков за счет других.
Итак, вы играете в монополию с друзьями Бобом, Джо и Эдом. Вы быстро строите свою империю и занимаете долю рынка по экспоненте. Ваши противники слабеют, и вы начинаете чувствовать запах крови (в переносном смысле). Ваш приятель Боб вложил все свои деньги в блокировку как можно большего количества недорогих объектов недвижимости, но он не получает высокую отдачу от инвестиций, как он ожидал. Боб, как удар неудачи, приземляется на твой Настил и исключается из игры.
Теперь игра переходит от дружелюбной игры в кости к серьезному бизнесу. Боб стал примером неудачи, а Джо и Эд не хотят в конечном итоге стать "тем парнем". Итак, будучи ведущим игроком, вы внезапно становитесь врагом. Джо и Эд начинают практиковать сделки "за столом", денежные вливания за спиной, недооцененный обмен домами и вообще все, что может ослабить вас как игрока, пока один из них не поднимется на вершину.
Затем, вместо того, чтобы выиграть один из них, процесс начинается заново. Внезапно, конечный набор правил становится движущейся целью, и игра вырождается в тип социальных взаимодействий, которые составляли бы основу каждого высокого реалити-шоу со времен Survivor. Почему, потому что правила меняются, и нет единого мнения о том, как / почему / что они должны представлять, и что более важно, никто не принимает решения. Каждый игрок в игре, в этот момент, устанавливает свои собственные правила, и за этим следует хаос, пока два игрока не слишком устают, чтобы поддерживать шараду и медленно сдаваться.
Таким образом, если бы свод правил для игры точно представлял собой единичную единицу, монопольный свод правил был бы примером злоупотребления.
Как это относится к программированию?
Помимо всех очевидных проблем с безопасностью потоков и синхронизацией, которые возникают в изменчивых синглетах... Если у вас есть один набор данных, который может одновременно считываться / обрабатываться несколькими различными источниками и существует в течение времени жизни приложения, вероятно, сейчас самое время сделать шаг назад и спросить: "Использую ли я здесь правильный тип структуры данных".
Лично я видел, как программист злоупотребляет синглтоном, используя его как некое скрученное многопоточное хранилище базы данных в приложении. Работая над кодом напрямую, я могу засвидетельствовать, что он был медленным (из-за всех блокировок потоков, необходимых для обеспечения его потоковой безопасности) и кошмаром для работы (из-за непредсказуемой / прерывистой природы ошибок синхронизации), и почти невозможно проверить в условиях "производства". Конечно, система могла бы быть разработана с использованием опроса / сигнализации для преодоления некоторых проблем с производительностью, но это не решило бы проблемы с тестированием и зачем беспокоиться, когда "настоящая" база данных уже может выполнять те же функции в гораздо более надежной среде. / масштабируемый способ.
Синглтон - это только вариант, если вам нужно то, что обеспечивает синглтон. Доступный только для чтения экземпляр объекта. Это же правило должно касаться и свойств / элементов объекта.
Смотрите Википедию Singleton_pattern
Некоторые люди считают, что он также является анти-паттерном, который использует его чрезмерно, вводя ненужные ограничения в ситуациях, когда отдельный экземпляр класса фактически не требуется.[1][2][3][4]
Список литературы (только соответствующие ссылки из статьи)
- ^ Алекс Миллер. Образцы, которые я ненавижу № 1: Синглтон, июль 2007
- ^ Скотт Денсмор. Почему синглтоны злые, май 2004
- ^ Стив Йегге. Синглтоны считаются глупыми, сентябрь 2004
- ^ JB Рейнсбергер, IBM. Используй свои синглеты с умом, июль 2001
Мой ответ о том, как синглтоны плохие, всегда звучит так: "Их трудно сделать правильно". Многие из основополагающих компонентов языков являются синглетонами (классы, функции, пространства имен и даже операторы), как и компоненты в других аспектах вычислений (локальный хост, маршрут по умолчанию, виртуальная файловая система и т. Д.), И это не случайно. Хотя они время от времени доставляют неприятности и разочарование, они также могут заставить многое работать намного лучше.
Два самых больших провала, которые я вижу, это: относиться к нему как к глобальному и не определить замыкание синглтона.
Все говорят о Синглтоне как о глобале, потому что они в основном так и есть. Тем не менее, большая (к сожалению, не вся) вредность в глобальном масштабе проистекает не из того, что он глобальный, а из-за его использования. То же самое касается синглетонов. На самом деле тем более, что "единый экземпляр" на самом деле не должен означать "глобально доступный". Это более естественный побочный продукт, и, учитывая все плохое, что, как мы знаем, проистекает из него, мы не должны спешить использовать глобальную доступность. Когда программисты видят Singleton, они, кажется, всегда получают к нему доступ напрямую через метод его экземпляра. Вместо этого вы должны перейти к нему так же, как к любому другому объекту. Большая часть кода даже не должна знать, что имеет дело с синглтоном (слабая связь, верно?). Если только небольшой кусочек кода обращается к объекту, как к глобальному объекту, большой вред отменяется. Я рекомендую применять его, ограничивая доступ к функции экземпляра.
Контекст Singleton также очень важен. Определяющей характеристикой Singleton является то, что существует "только один", но на самом деле он "только один" в некотором контексте / пространстве имен. Обычно это один из них: один на поток, процесс, IP-адрес или кластер, но также может быть один на процессор, машину, пространство имен языка / загрузчик классов / что угодно, подсеть, Интернет и т. Д.
Другая, менее распространенная ошибка - игнорировать образ жизни Синглтона. Тот факт, что существует только один, не означает, что синглтон является всемогущим "всегда был и всегда будет", и это не всегда желательно (объекты без начала и конца нарушают все виды полезных допущений в коде и должны использоваться только в самых отчаянных обстоятельствах.
Если вы избежите этих ошибок, Singletons все еще могут быть PITA, но они готовы увидеть, что многие худшие проблемы значительно смягчены. Представьте себе Java Singleton, который явно определяется как один раз для загрузчика классов (что означает, что ему нужна политика безопасности потоков) с определенными методами создания и уничтожения и жизненным циклом, который определяет, когда и как они будут вызваны, и чей метод "экземпляра" имеет защита пакета, так что к нему обычно обращаются через другие, неглобальные объекты. Все еще потенциальный источник проблем, но, конечно, гораздо меньше проблем.
К сожалению, вместо того, чтобы преподавать хорошие примеры того, как сделать Singletons. Мы учим плохим примерам, позволяем программистам некоторое время убегать, используя их, а затем говорим им, что это плохой шаблон проектирования.
Дело не в том, что сами синглеты плохие, а в шаблоне дизайна GoF. Единственный действительный аргумент, который является действительным, заключается в том, что шаблон проектирования GoF не подходит для тестирования, особенно если тесты выполняются параллельно.
Использование одного экземпляра класса является допустимой конструкцией, если в коде применяются следующие средства:
Убедитесь, что класс, который будет использоваться как синглтон, реализует интерфейс. Это позволяет реализовать заглушки или макеты с использованием одного и того же интерфейса.
Убедитесь, что Singleton является поточно-ориентированным. Это дано.
Синглтон должен быть простым по природе и не слишком сложным.
Во время выполнения вашего приложения, где синглтоны должны быть переданы данному объекту, используйте фабрику классов, которая создает этот объект и заставляет фабрику классов передавать экземпляр синглтона классу, который в этом нуждается.
Во время тестирования и для обеспечения детерминированного поведения создайте одноэлементный класс в качестве отдельного экземпляра в качестве самого фактического класса или заглушки / макета, который реализует его поведение, и передайте его как есть классу, который требует его. Не используйте фактор класса, который создает тестируемый объект, который нуждается во синглтоне во время теста, так как он пройдет единственный глобальный экземпляр этого объекта, что противоречит цели.
Мы использовали Singletons в наших решениях с большим успехом, которые можно тестировать, обеспечивая детерминированное поведение в параллельных потоках тестовых прогонов.
Я хотел бы рассмотреть 4 пункта в принятом ответе, надеюсь, кто-то может объяснить, почему я не прав.
Почему плохо скрывать зависимости в вашем коде? Уже есть десятки скрытых зависимостей (вызовы времени выполнения C, вызовы OS API, вызовы глобальных функций), и одноэлементные зависимости легко найти (поиск instance ()).
"Создание чего-то глобального, чтобы избежать его распространения, - это запах кода". Почему бы не передать что-то, чтобы не сделать это запахом кода?
Если вы пропускаете объект через 10 функций в стеке вызовов только для того, чтобы избежать одиночного вызова, это так здорово?
Принцип единой ответственности: я думаю, что это немного расплывчато и зависит от вашего определения ответственности. Уместным будет вопрос, почему добавление этой конкретной "ответственности" к классу имеет значение?
Почему передача объекта в класс делает его более тесно связанным, чем использование этого объекта в качестве одиночного изнутри класса?
Почему это меняет то, как долго длится государство? Синглтоны могут быть созданы или уничтожены вручную, поэтому элемент управления все еще там, и вы можете сделать время жизни таким же, как было бы время жизни не-одиночного объекта.
Относительно юнит-тестов:
- не все классы должны быть проверены модулем
- не все классы, которые должны быть проверены модулем, должны изменить реализацию синглтона
- если они действительно нуждаются в модульном тестировании и требуют изменения реализации, легко изменить класс с использования синглтона на передачу ему сингтона посредством внедрения зависимостей.
У Винса Хьюстона есть следующие критерии, которые кажутся мне разумными:
Синглтон следует рассматривать только в том случае, если выполнены все три из следующих критериев:
- Право собственности на один экземпляр не может быть назначено разумно
- Ленивая инициализация желательна
- Глобальный доступ не предусмотрен для
Если владение отдельным экземпляром, когда и как происходит инициализация, и глобальный доступ не являются проблемой, Singleton не достаточно интересен.
Я не собираюсь комментировать доводы добра / зла, но я не использовал их с тех пор, как пришла весна. Использование внедрения зависимостей в значительной степени сняло мои требования к синглтону, сервис-локаторам и фабрикам. Я считаю, что это гораздо более производительная и чистая среда, по крайней мере, для того типа работы, которую я выполняю (веб-приложения на основе Java).
Синглтоны плохи с точки зрения пуриста.
С практической точки зрения, синглтон - это компромисс между временем разработки и сложностью.
Если вы знаете, что ваше приложение не сильно изменится, с ним все в порядке. Просто знайте, что вам может потребоваться рефакторинг, если ваши требования неожиданно изменятся (что в большинстве случаев вполне нормально).
Синглтоны иногда также усложняют юнит-тестирование.
В паттерне нет ничего неправильного, если он используется для некоторого аспекта вашей модели, который является по-настоящему единым.
Я полагаю, что обратная реакция вызвана ее чрезмерным использованием, что, в свою очередь, связано с тем, что это самый простой шаблон для понимания и реализации.
Синглтон - это шаблон, который можно использовать или использовать как любой другой инструмент.
Плохая часть синглтона - это обычно пользователь (или я должен сказать, что использование синглтона нецелесообразно для вещей, для которых он не предназначен). Крупнейший преступник использует синглтон в качестве фальшивой глобальной переменной.
Когда вы пишете код, используя синглтоны, скажем, регистратор или соединение с базой данных, и впоследствии обнаруживаете, что вам нужно более одного журнала или более одной базы данных, у вас возникают проблемы.
Синглтоны затрудняют переход от них к обычным объектам.
Кроме того, слишком просто написать не-поточно-безопасный синглтон.
Вместо того, чтобы использовать синглтоны, вы должны передавать все необходимые служебные объекты от функции к функции. Это можно упростить, если вы оберните их все во вспомогательный объект, например так:
void some_class::some_function(parameters, service_provider& srv)
{
srv.get<error_logger>().log("Hi there!");
this->another_function(some_other_parameters, srv);
}
Недавняя статья на эту тему Криса Рита в " Кодировании без комментариев".
Примечание. Кодирование без комментариев больше не действует. Тем не менее, ссылка на статью была клонирована другим пользователем.
Слишком много людей помещают объекты, которые не являются потокобезопасными, в шаблон синглтона. Я видел примеры DataContext ( LINQ to SQL), выполненного в одноэлементном шаблоне, несмотря на тот факт, что DataContext не является потокобезопасным и является просто объектом единицы работы.
Проблемы с синглетонами - это проблема увеличенного объема и, следовательно, связи. Нельзя отрицать, что в некоторых ситуациях вам нужен доступ к одному экземпляру, и это можно сделать другими способами.
Теперь я предпочитаю проектировать контейнер с инверсией управления (IoC) и позволять контейнеру контролировать время жизни. Это дает вам преимущество того, что классы, зависящие от экземпляра, не знают о том, что существует один экземпляр. Время жизни синглтона может быть изменено в будущем. Однажды с таким примером, с которым я недавно столкнулся, была простая настройка от однопоточного к многопоточному.
FWIW, если это PIA, когда вы пытаетесь выполнить юнит-тестирование, то это будет PIA, когда вы пытаетесь отладить, исправить ошибку или улучшить ее.
Синглтоны НЕ плохие. Плохо только тогда, когда вы делаете что-то глобально уникальное, что не является глобально уникальным.
Однако существуют "службы области приложения" (подумайте о системе обмена сообщениями, которая обеспечивает взаимодействие компонентов) - это CALLS для одиночного объекта, "MessageQueue" - класс, имеющий метод "SendMessage(...)".
Затем вы можете сделать следующее со всего места:
MessageQueue.Current.SendMessage (new MailArrivedMessage (...));
И, конечно же, сделать:
MessageQueue.Current.RegisterReceiver (это);
в классах, которые реализуют IMessageReceiver.
Одиночки не являются злом, если вы используете их правильно и минимально. Есть много других хороших шаблонов проектирования, которые в какой-то момент заменяют потребности в синглтоне (и также дают лучшие результаты). Но некоторые программисты не знают об этих хороших шаблонах и используют синглтон для всех случаев, что делает синглтон злом для них.
Вот еще одна вещь о синглетах, о которой еще никто не говорил.
В большинстве случаев "синглтонность" - это деталь реализации некоторого класса, а не характеристика его интерфейса. Обращение Контейнер Контроля может скрыть эту характеристику от пользователей класса; вам просто нужно пометить свой класс как синглтон (с @Singleton
аннотация в Java например) и все; IoCC сделает все остальное. Вам не нужно предоставлять глобальный доступ к вашему экземпляру синглтона, потому что доступ уже управляется IoCC. Таким образом, нет ничего плохого в IoC Singletons.
Предполагается, что GoF Singletons в отличие от IoC Singletons предоставляют "единство" в интерфейсе с помощью метода getInstance(), и поэтому они страдают от всего, что сказано выше.
Во-первых, класс и его сотрудники должны сначала выполнить свое предназначение, а не сосредотачиваться на учениках. Управление жизненным циклом (когда экземпляры создаются и выходят за рамки) не должно входить в сферу ответственности cladses. Для этого рекомендуется создать или настроить новый компонент для управления зависимостями с помощью внедрения зависимостей.
Часто программное обеспечение усложняется, имеет смысл иметь несколько независимых экземпляров класса "singleton" с различным состоянием. В таких случаях использование кода для простого захвата синглтона является неправильным. Использование Singleton.getInstance() может подойти для небольших простых систем, но оно не работает / не масштабируется, когда может потребоваться другой экземпляр того же класса.
Ни один класс не должен рассматриваться как одноэлементный, а скорее должен быть приложением его использования или того, как он используется для настройки зависимостей. Для быстрого и неприятного это не имеет значения - просто жесткое кодирование говорит, что пути к файлам не имеет значения, но для более крупных приложений такие зависимости должны быть разложены и более соответствующим образом управляться с помощью DI.
Проблемы, которые синглтон вызывают в тестировании, являются признаком их жестко запрограммированного варианта использования / среды. Набор тестов и множество тестов являются отдельными и отдельными, что несовместимо с жестким программированием одиночного кода.
Поскольку они в основном являются объектно-ориентированными глобальными переменными, вы обычно можете создавать свои классы таким образом, чтобы они вам не нужны.