December
2005
Паттерн Singleton (Одиночка)
Posted in: Паттерны проектирования |
English version of this article you can find here.
Этим постом я открываю цикл статей, посвященных паттернам проектирования. Все написанное мной основывается исключительно на личном опыте.
Паттерны проектирования - это описание некоторых проблем, возникающих во время объектно-ориентированного проектирования, а также способов их решения (как практических, так и теоретических). Иными словами - это примеры правильных подходов к решению типичных задач проектирования.
Одним из самых распространенных паттернов является Singleton (Одиночка). Задача этого паттерна ограничить количество экземпляров некоторого класса. Зачем это может понадобиться? Об этом читайте ниже. Реализовать же паттерн Singleton на языке Java можно следующим образом:
public final class Singleton {
private static Singleton _instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (_instance == null)
_instance = new Singleton();
return _instance;
}
}
Теперь приведу некоторые объяснения по поводу реализации.
Конструктор класса необходимо объявить с модификатором видимости private. Это предотвратит создание экземпляров класса как с помощью класса Singleton, так и с помощью его наследников. В связи с этим к объявлению класса смело можно дописать модификатор final.
Метод getInstance() создаст ровно один экземпляр класса Singleton. Этот метод объявлен как synchronized. Сделано это вот почему. В многопоточных программах при одновременном вызове метода getInstance() из нескольких потоков можно создать несколько экземпляров класса Singleton. А должен остаться только один!
От модификатора synchronized можно избавиться. Для этого _instance нужно проинициализировать:
private static final Singleton _instance = new Singleton(),
а в методе getInstance() убрать конструкцию “if”.
Но использование поздней инициализации (lazy initialization) предпочтительнее в случае, если создание экземпляра класса занимает много времени.
Вот собственно и все…
Ах, да! Зачем нам это нужно.
Мне приходится чаще всего использовать этот паттерн при работе с конфигурацией. Иногда конфигурацию программы удобно хранить в файле. Допустим, это будет простой текстовый файл “props.txt” со строками типа “ключ=значение”. Нам нужно гарантировать, что конфигурация в программе будет в единственном экземпляре. Вторую мы бы и так не создали, но нужно запретить это делать пользователю класса. Итак,
import java.util.*;
import java.io.*;
public class Configuration {
private static Configuration _instance = null;
private Properties props = null;
private Configuration() {
props = new Properties();
try {
FileInputStream fis = new FileInputStream(
new File(“props.txt”));
props.load(fis);
}
catch (Exception e) {
// обработайте ошибку чтения конфигурации
}
}
public synchronized static Configuration getInstance() {
if (_instance == null)
_instance = new Configuration();
return _instance;
}
// получить значение свойства по имени
public synchronized String getProperty(String key) {
String value = null;
if (props.containsKey(key))
value = (String) props.get(key);
else {
// сообщите о том, что свойство не найдено
}
return value;
}
}
Теперь для работы с конфигурацией можно использовать конструкцию вида:
String propValue = Configuration.getInstance()
.getProperty(propKey).
Если имена свойств в “props.txt” меняться не будут, можно описать их в классе таким образом:
public static final String PROP_KEY = “propKey”,
а значения получать так:
String propValue = Configuration.getInstance()
.getProperty(Configuration.PROP_KEY).
Паттерн Singleton полезен не только при работе с конфигурациями. Его можно использовать также при написании ConnectionPool, Factory и других вещей.
Вот теперь точно все.
35 Comments »
RSS feed for comments on this post. TrackBack URI
Очень полезная страничка!! Спасибо!!
Спасибо. Написано доступно и интересно.
Не плохо было бы убрать synchronized со всей функции, т.к. не очень optimized ![]()
Уже не первый раз Гугль кидает меня с моими вопросами именно сюда). Спасибо автору сайта.
а в какой момент выполняются все инициализации, которые до конструктора находятся? при загрузке класса? а что такое загрузка класса?
А всетаки, зачем это нужно? В примере мы имеем объект, содержащий поле props. Почему бы не сделать props статическим полем и выдавать его статическим методом (в котором если надо производить инициализацию props)?
очень доходчивый пример, буквально таки на пальцах! Спасибо
В книге GoF написано, что они этот паттерн в 90х холостяком называли (solitaire). ![]()
2Oleg: если ты имеешь в виду double checked synchronization, то это не работает и заслуживает отдельной статьи.
Если же имеется в виду использование synchronized блока, то это наоборот менее эффективно, чем синхронизированный метод.
Хороший пример, я раньше встречал какой-то невменяемый, а логика этого понятна.
Поясните, пожалуйста, почему не требуется synchronized на getProperty ?
А какая необходимость в:
private static Singleton _instance = null; ?
В смысле, почему бы не делать ВСЕГДА :
private static Singleton _instance = new Singleton(); ?
2 Vlad
потому что, создание обьекта должно быть в методе getInstance()для того, чтобы создавался тока один обьект, ведь этот паттерн для этого и предназначен ![]()
а почему бы не сделать:
private static final INSTANCE = new Singelton();
private Singelton(){…}
public static void getInstance(){
return INSTANCE;
}
принципиально то же самое что в примере, но немного изящнее и понятнее, и позволяет отказаться от проверки на null.
статья может и понятная. но человеку недавно начавшему изучать, вряд ли по зубам…..
Супер! В нете много статей и книг по паттернам, все они так зпутанно объясняют, а вот здесь кратко , с кодом да ещё,очень понятно и пример выбран понятный!
Автору респект!
Автору космический THANKS!
Спасиба автору.Понимат легко.ищё жду такие стате
To Pavel, To Vlad:
Чтобы не выполнять логику конструктора раньше времени - то есть раньше первого обращения. А вдруг в конструкторе надо зачитать и обработать длинющий файл?…
To Olka
Всё равно статическое поле инициализируется только при загрузке класса, а класс загружается только при первом к нему обращении. Т.к. обращения к классу Configuration делаются только при необходимости прочитать конфигурацию, то вариант со статическими методами работал бы так же, как Singleton. Так что это не тот случай, когда без Синглтона нельзя обойтись (например, когда нужно наследоваться). Но, всё-таки его использование здесь - это хороший стиль.
Как-то некрасиво получается…а нельзя сделать один класс Singleton, а потом просто наследоваться от него, а не писать реализацию синглтона в каждом классе, использующем эту идиому снова и снова чтобы не дублировать код? Точно знаю, что в C++ так делается (правда, не без использования шаблона класса), что выглядит поизящнее…только, вот, насчёт Jav’ы не уверен.
Автор ошибся и это очевидно. При большой нагрузке (старт сотен потоков плюс долгая инициализация) вылезут проблемы.
Верное, классическое, решение - вот:
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
Или private static final instance = new Singelton(); и дальше по тексту.
Отличия между этими решениями очевидны, но, боюсь, автор его не совсем осознает.
Как итог, я бы не стал доверять этому циклу статей. И вообще, ИМХО стоит читать нормальную документацию.
Не в обиду автору, мы все такими были.
Дабы на меня не обижались, дескать пришел, надул щеки и ушел, вот первая-же попавшаяся на глаза ссылка, в которой изложена суть вопроса
habrahabr.ru/blogs/complete_code/27108/
В общем, ИМХО, рано автору писать статьи
К сожалению мне трудно следить за актуальностью статей, которые я писал в 2005 году.
Но намек понял - буду стараться.
Простите, не обратил внимание на дату статьи.
Да, думаю между Вами в 2005-м и сегодняшним специалистом огромная разница.
Но все-же, пересмотрите статьи, которые Вы писали ранее, люди их смотрят.
Спасибо за Ваш отзыв. Постараюсь найти время для приведение в порядок ресурса.
Вот ссылка по теме, может кого-то заинтересует - http://www.javenue.info/post/83
знание патернов - унылое говно. Если хорошо знаешь среду и язык, так без знания патерна логично напишешь аналогию или копию за пару секунд.
jenik: никто не говорит, что без паттернов нельзя прожить. Статьи по паттернам - это такое же обучение, как, например, изучение синтаксиса языка или возможностей среды программирования.
Паттерны проектирования более высокоуровневые и не такие формализованные, как синтаксис языка, поэтому часто они вызывают споры у программистов. Но лично я не считаю, что это ненужное занятие, так как пноимание причин их создания позволяют по-другому взглянуть на ООП.
2 sdv
В 2009 можно было бы и предложить вариант через AtomicReference:
public class Singleton {
private static final AtomicReference SINGL_REF = new AtomicReference();
private Singleton() {
}
public static final Singleton getInstance() {
if (SINGL_REF.get() == null) {
SINGL_REF.compareAndSet(null, new Singleton());
}
return SINGL_REF.get();
}
}
Да, статьи очень хорошие.
Никто не может быть безупречен…
СПС
Люди их читают…мы в ответе за тех, кого приручили…Автор - учтите это:)
2 sdv
Зачем нужно
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
Почему нельзя сделать
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
}
return instance;}???
спс на пальцах прям осталось применить
Костя, синглтон - это красивый способ обойти запрет на глобальные переменные. Кроме того в паттернах хватает других боков наравне с этим. Не мучай себя и других этим. Имхо авторы паттернов никогда не проводили должного исследования на предмет решения постулиуемых проблемм. Для этого нужно поднять около 100 проектов и чтоб 80 из них или около того дали устойчивый статистический результат. А кто это делал? Неужто не было нужды никогда выбрасывать написаные тобой паттерны и писать новые после изменения требования или ещё чего такого рода?
2 Алекс(Чёрный):
>> Неужто не было нужды никогда выбрасывать написаные тобой паттерны и писать новые после изменения требования или ещё чего такого рода?
Да постоянно так происходит. Ну и потом я не пишу паттернами. Просто стараюсь писать нормальный код и рефакторить, если что-то не нравится.
Потом что-то вырисовывается и это “что-то” иногда подпадает под описание какого-то паттерна.