13
December

Паттерн 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 Singleton _instance = new Singleton(),

а в методе getInstance() убрать конструкцию “if”.
Вот собственно и все…
Ах, да! Зачем нам это нужно.
Мне приходится чаще всего использовать этот паттерн при работе с конфигурацией. Иногда конфигурацию программы удобно хранить в файле. Допустим, это будет простой текстовый файл “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 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 и других вещей.
Вот теперь точно все.

12 Comments »

RSS feed for comments on this post. TrackBack URI



Tatiana
February 26, 2006 #

Очень полезная страничка!! Спасибо!!

Igor
August 5, 2006 #

Спасибо. Написано доступно и интересно.

Oleg
November 30, 2006 #

Не плохо было бы убрать synchronized со всей функции, т.к. не очень optimized :)

Vile
December 12, 2006 #

Уже не первый раз Гугль кидает меня с моими вопросами именно сюда). Спасибо автору сайта.

Kat
February 6, 2007 #

а в какой момент выполняются все инициализации, которые до конструктора находятся? при загрузке класса? а что такое загрузка класса?

gdwz
April 10, 2007 #

А всетаки, зачем это нужно? В примере мы имеем объект, содержащий поле props. Почему бы не сделать props статическим полем и выдавать его статическим методом (в котором если надо производить инициализацию props)?

чувак
April 23, 2007 #

очень доходчивый пример, буквально таки на пальцах! Спасибо

Entry_N3
October 7, 2007 #

В книге GoF написано, что они этот паттерн в 90х холостяком называли (solitaire). :)

January 24, 2008 #

2Oleg: если ты имеешь в виду double checked synchronization, то это не работает и заслуживает отдельной статьи.
Если же имеется в виду использование synchronized блока, то это наоборот менее эффективно, чем синхронизированный метод.

February 14, 2008 #

Хороший пример, я раньше встречал какой-то невменяемый, а логика этого понятна.

radkat
February 29, 2008 #

Поясните, пожалуйста, почему не требуется synchronized на getProperty ?

Vlad
April 26, 2008 #

А какая необходимость в:
private static Singleton _instance = null; ?

В смысле, почему бы не делать ВСЕГДА :
private static Singleton _instance = new Singleton(); ?

Leave a comment

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>