Хороший случай для синглетонов? - PullRequest
7 голосов
/ 17 августа 2010

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

Кто-то предложил мне сделать их синглтонами, в чем дело за / против?

Ответы [ 10 ]

10 голосов
/ 17 августа 2010

Я считаю, что шаблон Singleton является наиболее ненадлежащим образом применяемым шаблоном проектирования. Я бы сказал, что за 12 лет разработки программного обеспечения я видел 5 подходящих примеров.

Я работал над проектом, в котором у нас была служба мониторинга системы, которая моделировала нашу систему с классом System (не путать со встроенным классом Java System), который содержал список Subsystem каждый. со списком Component с и т. д. Дизайнер изготовил System синглтон. Я спросил "Почему это Синглтон?" Ответ: «Ну, есть только одна система». «Я знаю, но почему вы сделали его Singleton? Не могли бы вы просто создать экземпляр одного экземпляра нормального класса и передать его нужным классам?» «Было проще просто позвонить getInstance() везде, а не передавать его». "О ..."

Этот пример типичен: синглтоны часто неправильно используются как удобный способ доступа к одному экземпляру класса, а не для принудительного применения уникального экземпляра по техническим причинам. Но это обходится дорого. Когда класс зависит от getInstance(), он навсегда привязан к реализации Singleton. Это делает его менее тестируемым, повторно используемым и настраиваемым. Это нарушает основное правило, которому я следую, и которое, вероятно, имеет общее название в некоторых эссе принципов разработки: классы не должны знать, как создавать свои зависимости. Зачем? Потому что это жестко кодирует классы вместе. Когда класс вызывает конструктор, он привязывается к реализации. getInstance() ничем не отличается. Лучшая альтернатива - передать интерфейс в класс, а что-то еще может сделать вызов конструктора / getInstance() / factory. Вот тут-то и внедряются фреймворки внедрения зависимостей, такие как Spring, хотя они и не нужны (просто очень приятно иметь).

Так, когда уместно использовать Синглтон? В том редком случае, когда создание более чем одного объекта буквально испортило бы приложение. Я не говорю о создании двух Земель в приложении Солнечной системы - это просто ошибка. Я имею в виду, где есть некоторый аппаратный или программный ресурс, который взорвет ваше приложение, если вы вызовете / выделите / создадите его более одного раза. Даже в этом случае классы, использующие Singleton, не должны знать, что это Singleton. Должен быть один и только один вызов getInstance(), который возвращает интерфейс, который затем передается конструкторам / установщикам классов, которые в этом нуждаются. Я предполагаю другой способ сказать, что вы должны использовать Синглтон для его «единственности», а не для «глобально доступной».

Кстати, в этом проекте я упомянул, где System был Синглтоном ... Ну, System.getInstance() был добавлен по всей базе кода вместе с несколькими другими неподходящими синглетонами. Год спустя появились новые требования: «Мы развертываем нашу систему на нескольких сайтах и ​​хотим, чтобы служба мониторинга системы могла отслеживать каждый экземпляр». Каждый экземпляр ... хммм ... getInstance() не собирается его резать: -)

8 голосов
/ 17 августа 2010

Эффективная Java говорит:

Singletons typically represent some system component that is intrinsically 
unique, such as a video display or file system.

Так что, если ваш компонент гарантирует один экземпляр для всего приложения и , у него есть какое-то состояние , имеет смысл сделатьэто синглтон

В вашем случае настройки приложения - хороший кандидат на синглтон.

С другой стороны, класс может иметь статические методы, только если вы хотите сгруппировать определенные функции вместе, такие как служебные классы, примеры в jdk: java.util.Arrays java.util.Collections.У них есть несколько связанных методов, которые воздействуют на массивы или коллекции

1 голос
/ 17 августа 2010

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

0 голосов
/ 18 августа 2010

Эффективная Java говорит:

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

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

(Выше заимствовано заимствовано у naikus)

Во многих случаях описанная выше ситуация может быть обработана «утилитарным классом», в котором все методы являются статическими.Но иногда Singleton по-прежнему предпочтительнее.

Основная причина защиты Singleton над классом Static Utility заключается в том, что для его настройки требуются немалые затраты.Например, если ваш класс представляет файловую систему, он потребует некоторой инициализации, которую можно поместить в конструктор Singleton, но для Static Utility Class нужно будет вызвать статический инициализатор.Если некоторые исполнения вашего приложения могут никогда не получить доступ к файловой системе, тогда с помощью Static Utility Class вы все равно заплатили за его инициализацию, даже если вам это не нужно.С Singleton, если вам никогда не нужно создавать его экземпляр, вы никогда не вызываете код инициализации.

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

0 голосов
/ 17 августа 2010

Вы должны использовать синглтоны для модульности.

Представьте в синглтоне следующие объекты:

Printer prt;
HTTPInfo httpInfo;
PageConfig pgCfg;
ConnectionPool cxPool;

Дело 1 . Представьте, что вы этого не сделали, но один класс для хранения всех статических полей / методов. Тогда вам придется иметь дело с одним большим пулом статики.

Дело 2 .
В вашем текущем случае вы разделили их на соответствующие классы, но в качестве статических ссылок. Тогда было бы слишком много шума, потому что каждое статическое свойство теперь доступно для вас. Если вам не нужна информация, особенно когда много статической информации, вам следует ограничить текущую область кода от просмотра ненужной информации.

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

Случай 3 Идентификатор ресурса.
Синглтоны позволяют легко масштабировать ресурсы. Допустим, теперь у вас есть единственная база данных для работы, и поэтому вы устанавливаете все ее настройки как статические в классе MyConnection. Что если наступит время, когда вам потребуется подключиться к нескольким базам данных? Если бы вы закодировали информацию о соединении как одноэлементное, улучшение кода было бы сравнительно проще.

Дело 4 Наследование.
Синглтон-классы позволяют себя расширять. Если у вас есть класс ресурсов, они могут совместно использовать общий код. Допустим, у вас есть класс BasicPrinter, который может быть создан как синглтон. Затем у вас есть LaserPrinter, который расширяет BasicPrinter.

Если бы вы использовали статические средства, то ваш код сломался бы, потому что вы не смогли бы получить доступ к BasicPrinter.isAlive как LaserPrinter.isAlive. Тогда ваш фрагмент кода не сможет управлять принтерами разных типов, если вы не разместите избыточный код.

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

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

Дело 5 Избегать информационных налетов. Существует так мало информации, которую нужно сделать доступной глобально, например, самые большие и самые маленькие целые числа. Почему Printer.isAlive разрешено делать трибуны? Только очень ограниченный набор информации должен быть разрешен для трибуны.

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

0 голосов
/ 17 августа 2010

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

Если должно быть один и точно один экземпляр объекта: используйте aSingleton.Под этим я подразумеваю, должна ли программа ОСТАНОВИТЬСЯ, если имеется более одного объекта.Хороший пример - если вы разрабатываете видеоигру, которая поддерживает рендеринг только на одно устройство вывода.Попытка открытия того же устройства снова (позор для жесткого кодирования!) Будет запрещена.ИМХО такой случай часто означает, что вы не должны использовать классы в первую очередь.Даже C позволяет вам легко инкапсулировать такую ​​проблему без создания класса Singleton, и при этом поддерживать элементы OO, которые применяются к singleton.Когда вы застряли в таком языке, как Java / C #, вам придется работать с одноэлементным шаблоном, если только статические члены не справятся с задачей самостоятельно.Вы все еще можете смоделировать другой путь через них.

Если это всего лишь случай взаимодействия объектов, вам, вероятно, следует подумать о более объектно-ориентированной.Вот еще один пример: допустим, что нашему рендерингу кода игровых движков требуется интерфейс для управления ресурсами и вводами, чтобы он мог выполнять свою работу.Вы можете сделать эти синглтоны и сделать что-то вроде ResourceManager.getInstance (). GetResource (name).Или вы можете создать класс приложения (например, GameEngine), который имеет ResourceManager и InputManager в качестве закрытых членов.Затем пусть GameEngine передает их по мере необходимости в методы кода рендеринга.Например, r.render (resourcemanager);.

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

Против синглетонов- многие способы использования синглетонов могут быть решены путем инкапсуляции его внутри родительского объекта и передачи объекта-члена другим методам объектов-членов.

Иногда правильное использование просто глупой глобальной переменной.Как использование GOTO или составного (и / или) условного оператора вместо того, чтобы писать один и тот же код обработки ошибок N раз с копированием и вставкой.


Код умнее, а не сложнее.

0 голосов
/ 17 августа 2010

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

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

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

0 голосов
/ 17 августа 2010

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

Самое простое, что вы можете сделать, - это статическая переменная:

public class Globals{
   public static final int ACONSTANT=1;

Это нормально и гарантирует, что у вас будет только один, и у вас не будет проблем с созданием экземпляра.Главный недостаток, конечно, заключается в том, что часто бывает неудобно запекать ваши данные. Также возникает проблема с загрузкой, если, например, ваша строка фактически создается, скажем, путем создания чего-то из внешнего ресурса (есть такжепопался с примитивами - если ваш статический финал представляет собой int, скажем, классы, которые зависят от него, скомпилируйте его встроенным образом, что означает, что перекомпиляция ваших констант не может заменить константы в приложении - например, с учетом public class B{ int i = Globals.ACONSTANT; } изменения Globals.ACONSTANT и перекомпиляциитолько Globals оставит Bi все еще = 1.)

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

В общем: статика

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

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

0 голосов
/ 17 августа 2010

Я думаю, вам не нужно создавать класс signleton.просто сделайте этот конструктор класса приватным.Как класс по математике в Java.

public final class Math {

    /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}

//static fields and methods

}
0 голосов
/ 17 августа 2010

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

Я считаю, что мое основное использование синглетонов обычно включает в себя класс, в котором есть статические методы, которые, возможно, после подготовки среды, создают экземпляр себя. Используя закрытые конструкторы и переопределяя Object.clone() для выброса CloneNotSupportedException, никакие другие классы не могут создать его новый экземпляр, или, если им когда-либо передали его экземпляр, они не могут его клонировать.

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

...