2010-08-17 3 views
7

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

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

+0

Нет, просто используйте глобальный. Если вам не нужно больше одного, не делайте больше одного. – GManNickG

ответ

8

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

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

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

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

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

+4

Как это обеспечивает преимущество перед статическим классом? – Borealid

+2

@Borealid Вы получаете контроль над строительством/разрушением класса, что может быть важно, если вы поддерживаете состояние. Статические классы должны быть неактивными, например, служебные классы. – Jake

+0

@Borealid В большинстве случаев преимущество - это доступность и элегантность. Экземпляр класса представляет собой фактическое инициализированное состояние. Где статические методы и поля имеют больше смысла, когда вы хотите сгруппировать определенные функции вместе, и вы не можете иметь их для определенного экземпляра или типа. Se мой обновленный ответ – naikus

0

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

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

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

+0

На самом деле есть действительно классный способ сделать синглтоны в Java, который не требует переопределения 'Object.clone()', в котором у частного внутреннего класса есть экземпляр одноэлементного класса. Когда вы вызываете метод getInstance() ', вы указываете его на внутренний экземпляр. – JBirch

1

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

+0

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

0

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

public final class Math { 

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

//static fields and methods 

} 
+0

Это не одноэлементный класс, а скорее использование абстрактного класса (который не может быть инстинктивным). Одноэлементный экземпляр создается точно один раз и имеет статический экземпляр getter. – f1sh

+0

Да, я знаю, что такое singleton class.he спрашивает, что он должен пойти на singleton, и я говорю, что не нужно идти за singleton. Но вы должны сделать свой конструктор класса закрытым для случайной реализации класса. – Vivart

+0

Vivart, бывают случаи, когда Singleton полезен. См. Мой ответ. – DJClayworth

0

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

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

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

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

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

Так в основном: Статика

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

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

0

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

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

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

+0

Контроль над созданием также помогает тестировать компоненты, зависящие от этого синглтона. –

0

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

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

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

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

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

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


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

10

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

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

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

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

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

+1

Итак, где пять подходящих ситуаций? – gustafc

+0

Ой, пожалуйста, проигнорируйте этот комментарий. – DJClayworth

+0

@gustacf: все они были классами C++, каждый из которых представлял собой единый набор пользовательских аппаратных средств, тесно связанных с оборудованием, которые не раз создавали экземпляр, чтобы аппаратное/программное обеспечение не работало. Старые школьные вещи, и все же, это не помешало нескольким процессуальным инстанциям не довести все до конца. – SingleShot

0

Для модуляции следует использовать синглтоны.

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

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

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

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

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

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

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

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

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

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

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

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

0

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

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

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

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

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

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

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

+0

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

+2

Синглтон не создает экземпляр экземпляра при загрузке класса, он создает его, когда запрашивается первый экземпляр. Если экземпляр никогда не запрашивается, код инициализации никогда не запускается. – DJClayworth

+0

@DJClayworth: точно. Экземпляр не создается до первого запроса getInstance() или любого другого метода, возвращающего экземпляр. – Izza

Смежные вопросы