CodeLAB
на главную карта сайта обратная связь

Популярные задачи:

#Отслеживание изменений файла. (37859 hits)
#Рисование 3D объекта. (35138 hits)
#Просмотр изображения во всплывающем окне. (89251 hits)
#Создание простейшей таблицы. (37129 hits)
#Утилиты. (114404 hits)
#qForms, библиотека типичного функционала валидации/построения/связки html-форм. (147300 hits)
#Таймер. (40781 hits)
#Простая геометрическая и текстовая анимация. (400925 hits)
#Бинарный поиск в массиве и его разновидности. (169241 hits)
#Пирамидальная сортировка. (203693 hits)
#Преобразование целых чисел в битовый массив. (37623 hits)
#Динамическое изменение цвета полоски прокрутки в IE5.5 и выше. (30922 hits)
#Вычисление минимального / максимального значения. (74388 hits)
#Поразрядная сортировка массива подсчетом. (133125 hits)
#Наибольший общий делитель. (192500 hits)
#Рисование тора. (34735 hits)
#Постепенное затемнение. (51349 hits)
#ООП на javascript: классы, наследование, инкапсуляция. (257609 hits)
#Относительный путь к файлу. (39741 hits)
#Программное создание ссылок. (99850 hits)


Главная >> Каталог задач >> Паттерны >> Порождающие >> Одиночка (Singleton)

Одиночка (Singleton)

Aвтор:
Дата:
Просмотров: 118729
реализации(java: 4шт...) +добавить

Имя

«Паттерн
Singleton»

Одиночка – паттерн, порождающий объекты.

Условия, Задача, Назначение

Гарантирует, что у класса будет не более 1 созданного экземпляра, предоставляет к нему глобальную точку доступа (обычно статический метод).

Мотивация

Очень часто бывает нужно хранить в программе некоторый объект – уникальным, т.е. чтобы всегда, когда запрашивался объект данного класса – возвращался один и тот же экземпляр. Подумайте – о таком объекте как соединение с базой данных. Во всех частях приложения нужно обеспечить доступ к базе, при этом будет неразумным каждый раз в процессе одного и того же функционирования соединяться с базой данных заново (одна из самых трудоемких операций). Поэтому необходимо всегда когда запрашивается данный объект-соединение – возвращать уже существующий один и тот же экземпляр.

Как показывает опыт, в каждом более-менее большом приложении целесообразно иметь немало таких объектов: в системе может быть много принтеров, но возможен лишь один спулер; должны быть только одна файловая система и единственный оконный менеджер; в цифровом фильтре может находиться только один аналого-цифровой преобразователь (АЦП); бухгалтерская система обслуживает только одну компанию и т.д.
Как гарантировать, что у класса есть единственный экземпляр и что этот экземпляр легко доступен? Глобальная переменная дает доступ к объекту, но не запрещает инстанцировать класс в нескольких экземплярах.

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

Признаки применения, использования паттерна Одиночка (Singleton)

Если:
  1. Должен быть ровно один экземпляр некоторого класса, легко доступный всем клиентам
  2. Единственный экземпляр должен расширяться путем порождения подклассов, и клиентам нужно иметь возможность работать с расширенным экземпляром без модификации своего кода.

Решение

Участники паттерна Одиночка (Singleton)

  1. Singleton - одиночка.
    Определяет операцию получения экземпляра (назовем ее Instance) - статический метод, который позволяет клиентам получать доступ к единственному экземпляру. Может нести ответственность за создание собственного уникального экземпляра.

Схема использования паттерна Одиночка (Singleton)

Клиенты получают доступ к экземпляру класса Singleton только через его операцию Instance. Таким образом, когда нужно создать, получить объект этого класса – клиент не инстанцирует его напрямую, а вызывает эту операцию Instance(статический метод) – и получает объект.

Вопросы, касающиеся реализации паттерна Одиночка (Singleton)

При реализации паттерна одиночка (singleton) необходимо рассмотреть следующие вопросы:
  1. Гарантирование единственного экземпляра.
    Паттерн одиночка устроен так, что тот единственный экземпляр, который имеется у класса, - самый обычный, но больше одного экземпляра создать не удастся. Чаще всего для этого прячут операцию, создающую экземпляры (защищенный или закрытый конструктор), за операцией класса (то есть за статической функцией-членом или методом класса), которая гарантирует создание не более одного экземпляра. Данная операция имеет доступ к переменной, где хранится уникальный экземпляр, и гарантирует инициализацию переменной этим экземпляром перед возвратом ее клиенту. При таком подходе можно не сомневаться, что одиночка будет создан и инициализирован перед первым использованием.
  2. Порождение подклассов Singleton. Основной вопрос не столько в том, как определить подкласс, а в том, как сделать, чтобы клиенты могли использовать только его единственный экземпляр, никак не создав других экземпляров. По существу, переменная, ссылающаяся на экземпляр одиночки, должна инициализироваться вместе с экземпляром подкласса. Простейший способ добиться этого - определить одиночку, которого нужно инстанцировать в операции Instance() класса Singleton.
    Другой способ выбора подкласса Singleton - вынести реализацию операции Instance из родительского класса и поместить ее в подкласс, т.е. разрешить переопределение этой операции. Это позволит программисту задать класс одиночки на этапе компиляции, но от клиента одиночка будет по-прежнему скрыт.
    Такой подход фиксирует выбор класса одиночки на этапе компиляции, затрудняя тем самым его подмену во время выполнения. Применение условных операторов для выбора подкласса увеличивает гибкость решения, но все равно множество возможных классов Singleton остается жестко «зашитым» в код. В общем случае ни тот, ни другой подход не обеспечивают достаточной гибкости.
    Искомой гибкости можно добиться за счет использования реестра одиночек. Вместо того чтобы задавать множество возможных классов Singleton в операции Instance(), одиночки могут регистрировать себя по имени в некотором всем известном реестре. Реестр сопоставляет одиночкам строковые имена (ключи). Когда операции Instance() нужен некоторый одиночка, она запрашивает его у реестра по имени. Начинается поиск указанного одиночки, и, если он существует, реестр возвращает его. Такой подход освобождает Instance от необходимости «знать» все возможные классы или экземпляры Singleton. Нужен лишь единый для всех классов Singleton интерфейс операций с реестром, api реестра т.е.:
     RegSingleton, api реестра [java]  ссылка
    1. package singleton;
    2.  
    3. import java.util.HashMap;
    4. import java.util.Map;
    5.  
    6. public class RegSingleton {
    7. private static RegSingleton _instance;
    8. private static Map _registry = new HashMap();
    9.  
    10. protected RegSingleton() { }
    11.  
    12. public static void Register(String name, RegSingleton obj) {
    13. _registry.put(name, obj);
    14. }
    15.  
    16. public static RegSingleton Instance() {
    17. if (_instance == null) {
    18. // User define this name in system property file
    19. // before program starts
    20. String name = System.getProperty("patterns.book.singletonName");
    21. _instance = RegSingleton.Lookup(name);
    22. }
    23. return _instance;
    24. }
    25.  
    26. protected static RegSingleton Lookup(String name) {
    27. return (RegSingleton) _registry.get(name);
    28. }
    29. }

    Операция Register регистрирует экземпляр класса RegSingleton (или его подклассов) под указанным именем. Чтобы не усложнять реестр, мы будем хранить в нем список объектов в виде пар «ключ-значение» (хеш-таблица). Каждый ключ (имя) отображается на один объект «одиночка». Операция Lookup ищет одиночку по ключу (имени), например – из данных property-файла.
    Но в какой момент подклассы RegSingleton (SingletonSub например) будут регистрировать себя? Одна из возможностей – конструктор, но, разумеется, конструктор не будет вызван, пока кто-то не инстанцирует класс, но ведь это та самая проблема, которую паттерн одиночка и пытается разрешить! Существуют некоторые обходные пути: в Java, например, это можно решить, задействовав статический блок инициализации, который просто лишь вызовет конструктор, создавая объект и ни к чему его не привязывая (метод Register его затем сам привяжет к регистру!):
     SingletonSub, через стат-инициализатор [java]  ссылка
    1. package singleton;
    2.  
    3. public class SingletonSub extends RegSingleton {
    4. // It's the only time and place when
    5. // its constructor invokes
    6. static {
    7. new SingletonSub();
    8. }
    9.  
    10. protected SingletonSub() {
    11. // put this subclass in common register with
    12. // this class name
    13. RegSingleton.Register(this.getClass().getName(), this);
    14. }
    15.  
    16. }
    Теперь класс RegSingleton не отвечает за создание одиночки. Его основной обязанностью становится обеспечение доступа к объекту-одиночке из любой части системы.

Результаты

Достоинства паттерна одиночка (Singleton):
  1. Контролируемый доступ к единственному экземпляру.
    Поскольку класс Singleton инкапсулирует свой единственный экземпляр, он полностью контролирует то, как и когда клиенты получают доступ к нему
  2. Уменьшение числа имен.
    Паттерн одиночка - шаг вперед по сравнению с глобальными переменными. Он позволяет избежать засорения пространства имен глобальными переменными, в которых хранятся уникальные экземпляры.
  3. Допускает уточнение операций и представления.
    От класса Singleton можно порождать подклассы, а приложение легко сконфигурировать экземпляром расширенного класса. Можно параметризировать приложение экземпляром того класса, который необходим во время выполнения.
  4. Допускает переменное число экземпляров.
    Очень важное достигнутое преимущество. Паттерн позволяет вам легко изменить свое решение и разрешить появление более одного экземпляра класса Singleton. Вы можете применять один и тот же подход для управления числом экземпляров, используемых в приложении. Изменить нужно будет лишь операцию, дающую доступ к экземпляру класса Singleton. Фактически, дается возможность контролировать количество инстанцированных экземпляров класса, фигурирующих где бы то ни было в приложении.
  5. Большая гибкость, чем у операций класса.
    Еще один способ реализовать функциональность одиночки - использовать операции класса, то есть статические члены. Но это препятствует изменению дизайна, если потребуется разрешить наличие нескольких экземпляров класса. Кроме того, статические члены не полиморфны, так что их нельзя будет переопределять в подклассах.

Пример

Предположим, что у нас есть имеется некоторая абстрактная фабрика SomeFactory. Абстрагируемся от продуктов фабрики, от логики ее работы и т.д. – здесь это совершенно не принципиально.
Существенно то, что приложению нужен лишь один экземпляр фабрики лабиринтов, и он должен быть доступен в любом коде, производящем любой из продуктов. Тут-то паттерн одиночка и приходит на помощь. Сделав фабрику SomeFactory одиночкой, мы сможем обеспечить глобальную доступность объекта, представляющего фабрику.

Нужно также сделать конструктор защищенным, чтобы предотвратить случайное инстанцирование, в результате которого будет создан лишний экземпляр: SomeFactory.

Теперь рассмотрим случай, что у нашей фабрики существуют подклассы: WiseFactory и QuickFactory. Конкретный подкласс фабрики, использующийся в приложении – определяется системными настройками. Все что нам нужно сделать – это добавить в операцию instance() все того же класса SomeFactory код выбора конкретной фабрики в зависимости от значения этого системного свойства – и конечно обеспечить подклассы фабрики защищенным конструктором (не то появится возможность напрямую инстанцировать фабрики, минуя все наши запреты): SomeFactory.

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

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

 

Известные применения паттерна Одиночка (Singleton)

Примером паттерна одиночка в Smalltalk-80 является множество изменений кода, представленное классом ChangeSet. Более тонкий пример – это отношение между классами и их метаклассами. Метаклассом называется класс класса, каждый метакласс существует в единственном экземпляре. У метакласса нет имени (разве что косвенное, определяемое экземпляром), но он контролирует свой уникальный экземпляр, и создать второй обычно не разрешается.

В библиотеке Interviews для создания пользовательских интерфейсов - паттерн одиночка применяется для доступа к единственным экземплярам классов Session (сессия) и WidgetKit (набор виджетов). Классом Session определяется главный цикл распределения событий в приложении. Он хранит пользовательские настройки стиля и управляет подключением к одному или нескольким физическим дисплеям. WidgetKit - это абстрактная фабрика для определения внешнего облика интерфейсных виджетов. Операция WidgetKit: instance () определяет конкретный инстанцируемый подкласс WidgetKit на основе переменной среды, которую устанавливает Session. Аналогичная операция в классе Session «выясняет», поддерживаются ли монохромные или цветные дисплеи, и соответственно конфигурирует одиночку Session.

Родственные паттерны

Паттерн одиночка (Singleton) целесообразно применять вместе со многими паттернами, например, такими так: абстрактная фабрика, строитель, прототип.



 

Реализации:

java(4)   +добавить

1) SomeFactory.java на java, code #433[автор:this]
2) SomeFactory.java, модификация на java, code #434[автор:this]
3) WiseFactory.java на java, code #435[автор:this]
4) QuickFactory.java на java, code #436[автор:this]