Синглтоны действительно так плохи?
Возможный дубликат:
Что такого плохого в синглетонах?
Понятно, что во многих случаях можно злоупотреблять многими шаблонами дизайна, и, как мама всегда говорила: "Слишком много хорошего не всегда хорошо!"
Я замечаю, что в эти дни я часто использую Singletons, и я боюсь, что могу сам злоупотреблять шаблоном дизайна и все глубже и глубже погружаться в привычку плохой практики.
Мы разрабатываем приложение Flex, которое имеет довольно большую иерархическую структуру данных, хранящуюся в памяти, пока пользователь работает над ней. Пользователь может загружать, сохранять, изменять и обновлять данные по требованию.
Эти данные централизованы с помощью класса Singleton, который объединяет пару ArrayCollections, Arrays, объектов значений и некоторых других собственных переменных-членов, предоставляемых через методы получения и установки.
Чтобы получить ссылку на наши данные из любого места в приложении, мы делаем весь тип метода Model.getInstance(), который, я уверен, всем знаком. Это гарантирует, что мы всегда получим в руки одну и ту же копию данных, так как при разработке мы говорили, что только один раз может существовать экземпляр в течение времени жизни приложения.
Из этого центрального хранилища данных мы можем, например, легко отправлять события изменения свойств и иметь несколько компонентов пользовательского интерфейса, которые ссылаются на центральные данные, обновлять свои отображения, чтобы отражать произошедшие изменения данных.
До сих пор этот подход был эффективным и оказался очень практичным в наших условиях.
Я обнаружил, однако, что я немного переусердствовал при создании новых классов. Такие вопросы, как, например, должен ли класс быть Синглтоном, или если им лучше управлять каким-либо другим способом, как, например, использование фабрики, например, иногда становятся немного сложными, с некоторой неопределенностью.
Где я могу провести черту с одиночками? Есть хорошее руководство для решения, когда использовать Singletons, а когда держаться подальше от них.
Кроме того, кто-нибудь может порекомендовать хорошую книгу по шаблонам дизайна?
12 ответов
Главное, что нужно помнить, это то, что шаблоны проектирования - это всего лишь инструмент, помогающий понять абстрактные понятия. Если у вас есть это понимание, то ограничивать себя определенным "рецептом" из книги бессмысленно и лишает вас возможности писать код, наиболее подходящий для ваших целей.
Тем не менее, чтение книг, таких как GoF, предоставит вам больше способов думать о проблемах, так что, когда придет время реализовать что-то самостоятельно, у вас будет более широкий набор подходов к решению проблемы.
В вашем случае, если использование синглтона имеет смысл в каждом случае, тогда идите вперед. Если это "что-то" подходит, и вы должны реализовать его каким-то неуклюжим способом, то вам нужно найти новое решение. Формирование не идеального рисунка похоже на вбивание квадратного колышка в круглое отверстие.
Учитывая, что вы говорите, что "этот подход был эффективным и доказал свою практичность в наших обстоятельствах", я думаю, у вас все хорошо.
Вот несколько хороших книг:
Gang of Four Book - классическая книга для дизайна выкроек
Head First Design Patterns - я слышал, это рекомендовано несколькими людьми в качестве альтернативы
Да, синглтоны плохие. Они плохие, потому что все, что они делают для вас, - это объединение двух свойств, каждое из которых является плохим в 95% случаев. (Что означало бы, что в среднем одиночные игры плохи в 99,75% случаев;))
Синглтон, как определено GoF, представляет собой структуру данных, которая:
- Предоставляет глобальный доступ к объекту, и
- Обеспечивает существование только одного экземпляра объекта.
Первый вообще считается плохой вещью. Нам не нравятся глобалы. Второе немного более тонкое, но, как правило, практически нет случаев, когда это разумное ограничение.
Иногда имеет смысл иметь только один экземпляр объекта. В этом случае вы решили создать только один. Вам не нужен синглтон, чтобы обеспечить его соблюдение.
И обычно, даже когда "имеет смысл" иметь только один экземпляр, оказывается, что он не имеет смысла в конце концов. Рано или поздно вам понадобится более одного регистратора. Или более одной базы данных. Или вам придется заново создавать ресурсы для каждого из ваших модульных тестов, а это значит, что мы должны иметь возможность создавать их по желанию. Это преждевременно убирает гибкость из нашего кода, прежде чем мы поймем последствия.
Синглтоны скрывают зависимости и увеличивают связь (каждый класс может потенциально зависеть от одноэлементного, что означает, что класс не может быть повторно использован в других проектах, если мы также не будем повторно использовать все наши синглтоны), и потому что эти зависимости не видны сразу (как параметры функции / конструктора)), мы их не замечаем и обычно не думаем об этом, когда создаем их. Так просто вставить одиночный код, он действует почти как локальная переменная и все такое, поэтому мы склонны часто их использовать, как только они появятся. И это делает их почти невозможно удалить снова. В итоге вы получите не код спагетти, а графы зависимостей спагетти. И рано или поздно ваши побочные зависимости будут означать, что синглтоны начинают зависеть друг от друга, а затем вы получаете циклические зависимости, когда одна попытка инициализируется.
Они чрезвычайно затрудняют юнит-тестирование. (Как вы тестируете функцию, которая вызывает функции для одноэлементного объекта? Мы не хотим, чтобы выполнялся фактический одноэлементный код, но как мы можем предотвратить это?
Да, синглтоны плохие.
Иногда вы действительно хотите глобальный. Тогда используйте глобальный, а не синглтон.
Иногда, очень, очень, очень редко, вы можете столкнуться с ситуацией, когда создание нескольких экземпляров класса является ошибкой, когда это невозможно сделать, не вызывая ошибок. (О единственном случае, о котором я могу подумать и даже надумать, это если вы представляете какое-то аппаратное устройство. У вас есть только один графический процессор, поэтому, если вы собираетесь сопоставить его с объектом в вашем коде, это имеет смысл, что может существовать только один экземпляр). Но если вы окажетесь в такой ситуации (и опять же, для акцентирования, ситуации, когда множественные экземпляры приводят к серьезным ошибкам, а не просто к ситуации, когда "я не могу придумать ни одного варианта использования для более чем одного экземпляра"), то принудительно применяйте это ограничение, но сделайте это, не делая объект глобально видимым.
Каждое из этих двух свойств может быть полезно в редких случаях. Но я не могу вспомнить ни одного случая, когда их комбинация была бы хорошей вещью.
К сожалению, многие люди поняли, что "Singletons являются OOP-совместимыми глобалами". Нет, они не. Они по-прежнему испытывают те же проблемы, что и глобальные, в дополнение к некоторым другим, совершенно не связанным с ними. Нет абсолютно никакой причины предпочесть синглтон старому миру.
Разработчики программного обеспечения, кажется, довольно равномерно разделены на два лагеря, в зависимости от того, предпочитают ли они идеалистический стиль кодирования или прагматичный:
- Идеалист: Никогда не используйте шаблон синглтона.
- Прагматичный: избегайте одноэлементного шаблона.
Лично я за прагматичный подход. Иногда имеет смысл нарушать правила, но только если вы действительно понимаете, что делаете, и готовы принять связанные с этим риски. Если вы можете ответить "да" на приведенные ниже вопросы, касающиеся вашего конкретного варианта использования, шаблон синглтона может принести некоторые практические преимущества.
- Является ли синглтон внешним по отношению к вашему приложению? Базы данных, службы очередей и ESB - все это идеально допустимые примеры макросов одноэлементного шаблона.
- KISS: у тебя все приложение ограничено 2-3 внутренними синглетонами?
- СУХОЙ: Являются ли эти синглеты по своей природе глобальными и, следовательно, приведут ли к необходимости вставлять ссылки почти на каждый объект в вашем приложении? (например, регистратор или компонент-посредник)?
- Ваши синглтоны зависят только друг от друга и / или от операционной среды?
- Обеспечены ли правильные последовательности запуска и выключения для каждого синглтона, включая соображения управления памятью? Например, для пула потоков в стиле "Grand Central" могут потребоваться методы экземпляра Run() и Shutdown() в main(), чтобы гарантировать выполнение задач только в том случае, если объекты, с которыми они работают, находятся в допустимом состоянии.
Синглтоны не убивают программы, программисты убивают программы.
Как и любая программная конструкция, при правильном использовании вы не будете стрелять себе в ногу.
Рекомендуемые книги хороши, но они не всегда дают достаточный фон, который приходит с опытом, когда вы можете сделать выбор в пользу Singleton.
Этот опыт приходит только тогда, когда вы обнаружите, что Singleton - плохой выбор, когда вам нужно иметь несколько экземпляров, и внезапно у вас возникает много проблем с внедрением ссылок на объекты повсюду.
Иногда лучше пойти дальше и иметь ссылки на объекты на месте, но тот факт, что вы используете Singleton вообще, помогает определить масштаб проблемы, с которой вы столкнетесь, если вам придется реорганизовать ее в другой дизайн. Что я считаю очень хорошей вещью: то есть просто наличие класса (даже если он плохо спроектирован) дает некоторую способность видеть последствия изменения класса.
Мы начали проект, в котором перед нами стоит один и тот же вопрос, а именно, как получить доступ к модели и особенно к ее корневому элементу. Проект не приложение Flex, а игра! веб-приложение, но это не имеет значения на самом деле.
Хорошо иметь единственный объект в системе, проблема в том, как получить к нему доступ. Таким образом, дискуссия о синглтоне связана с понятием внедрения зависимостей (DI), и как получить объекты.
Основными аргументами в пользу DI являются следующие:
- тестируемость и издевательство
- отделение объекта от использования (что может привести к управлению жизненным циклом)
- разделение интересов
Возможные подходы для DI (см. Классическую статью от Фаулера):
- передать объект в параметрах метода
- сервисный локатор
- DI Framework
С этой точки зрения шаблон синглтона - это просто вид сервисного локатора, например Model.getInstance()
,
Но для обеспечения максимальной гибкости перед лицом будущих изменений, ссылка на уникальный объект должна передаваться как можно больше и получаться с Model.getInstance()
только когда это необходимо. Это также даст более чистый код.
На мой взгляд, использование Singletons напрямую сигнализирует о недостатке дизайна. Причина в том, что они позволяют обойти обычные механизмы создания и уничтожения объектов, встроенные в C++. Если объекту нужна ссылка на другой объект, он должен либо передать ссылку на него при создании, либо создать новый экземпляр для него внутри. Но когда вы используете синглтон, вы явно запутываете цикл создания и демонтажа. С этим связана проблема, заключающаяся в том, что чрезвычайно трудно контролировать время жизни синглтона. В результате многие пакеты, которые включают в себя универсальные одноэлементные реализации, также включают неуклюжие менеджеры времени жизни объектов и тому подобное. Иногда мне интересно, не существуют ли они просто для управления синглетонами.
По сути, если вам нужно использовать объект во многих местах, он должен быть явно создан на самой высокой общей точке в стеке, а затем передан по ссылке всем, кто его использует. Иногда люди используют Singletons, потому что у них возникают проблемы с передачей нескольких аргументов новым потокам, но не соглашайтесь на это, явно определите свои аргументы потока и передайте их новому потоку таким же образом. Вы обнаружите, что ваша программа работает намного чище, и нет никаких неприятных сюрпризов из-за статической зависимости инициализации или ошибочного удаления.
Синглтоны конечно не плохие. У них есть свои применения, некоторые из них очень хороши. Неопытные разработчики склонны злоупотреблять синглетонами, так как это часто первый шаблон дизайна, о котором они узнают, и это довольно просто, поэтому они разбрасывают его повсюду, не задумываясь о последствиях.
Каждый раз, когда вы хотите использовать синглтон, постарайтесь понять, почему вы это делаете, и каковы преимущества и недостатки использования этого шаблона.
Синглтоны действительно создают глобально доступный набор "вещей" (данных или методов), и я думаю, что большинство людей согласятся с тем, что использование слишком большого количества глобальных переменных не является хорошей идеей. Весь смысл классов и объектной ориентации состоит в том, чтобы группировать вещи в отдельные области, а не просто разбрасывать все в одно огромное глобальное пространство.
Один из "шаблонов", который я предпочитаю отдавать предпочтение одиночкам, - это пропускать нужные предметы сверху вниз. Я создаю их один раз на этапе инициализации приложений и пропускаю через все объекты, которым необходим доступ к ним. Он имитирует часть "одного творения" шаблона синглтона, но без "глобальной" части.
Весь смысл синглтона в том, что он предназначен для объектов, где только 1 должен существовать. Вы упоминаете набор классов управления данными. Возможно, стоит подумать, что на самом деле существуют случаи, когда приложение может захотеть создать 2 набора классов управления данными, поэтому, возможно, принудительное использование одиночного кода не совсем правильно. Вместо этого, если вы создали эти классы данных при инициализации приложения и передали их, вы будете создавать только 1 набор, поскольку это то, что требуется вашему текущему приложению, но вы оставляете открытым возможность того, что в какой-то момент вам понадобится второй набор Вы можете легко создать их. Кроме того, классы управления данными должны быть действительно глобально доступными из любой точки приложения. Я думаю, что нет, вместо этого они, вероятно, должны быть доступны только с уровня доступа к данным более низкого уровня.
Некоторые люди рекомендовали книгу GOF. Я бы сказал, да, это отличная книга, но сначала попытайтесь сначала найти книгу по общей архитектуре, сначала прочитайте о 2/3/n-уровневом дизайне, инкапсуляции, абстракции и подобных принципах. Это даст вам более прочную основу для понимания правильного использования шаблонов, о которых говорит GOF.
[Правка. Другой вариант, когда одноэлементный вариант может быть полезен, - это когда вам нужна единая точка доступа к чему-либо, но детали реализации могут на самом деле быть более чем одной вещью. Вызывающей стороне не нужно знать, что под прикрытием их запрос на одноэлементный объект фактически разрешается для нескольких доступных объектов, и один возвращается. Я думаю о чем-то вроде пула потоков здесь, где использование идет, эй, просто принеси мне поток, мне нужно 1, но мне все равно, какой]
Я знаю, что это старая тема, но никто, казалось, не упомянул фактическую модель, которая соответствует тому, что пытался сделать ОП. То, что, по моему мнению, он описывает необходимость, называется Образцом Посредника. SourceMaking - фантастический сайт для изучения / ссылок на этот вид информации. Определенно, я хочу познакомить людей с шаблонами программного обеспечения. Кроме того, как правило, это хорошая идея, чтобы не согласиться с тем, что любой шаблон дизайна по своей природе обязательно является добром или злом. У них у всех есть свое применение, и дело только в том, когда и где их использовать. Люди, которые утверждают, что никогда не используют Singletons, для меня, не понимают их полезности.
Нет, они не обязательно плохие.
Что касается книги, нужно начать с классики.
Google, похоже, убежден, что Singletons - плохая идея.
Это не значит, что все, что делает Google, является идеальным, или что каждое их мнение является концом любого аргумента, но они зашли так далеко, что написали этот детектор Singleton, чтобы искоренить их. Решайся сам.
Синглтоны не такие уж и плохие. Если у вас есть много связанных синглетонов, и вы можете заменить / объединить несколько из них, используя Фабрику, не теряя ничего, что вас волнует, тогда вы должны это сделать.
Что касается книг, то здесь есть своего рода канон.