Метафора
Представьте, что Вам нужно организовать в городе телефонную связь. Можно протянуть кабеля от каждого жителя к каждому, но такое решение явно имеет недостаток при добавлении нового жителя, т.к. придётся тянуть от него кабель до всех других. Гораздо дешевле создать в городе телефонную станцию, к которой будет подсоединен каждый житель. Для того чтобы позвонить нужно будет связаться со станцией, а далее станция сделает перенаправление к любому подключенному к ней жителю. Телефонная станция в данном контексте является одиночкой, смысл в том, что когда вы говорите «Мне нужна телефонная станция», вам бы отвечали «Вот она, держи», а не «Давай создадим её заново». Одиночка всегда один.
Назначение
Гарантирует, что у класса всегда будет существовать только один экземпляр, и предоставляет глобальную точку доступа к нему.
Плюсы:
- Контролируемый доступ к единственному экземпляру
Недостатки паттерна:
- Использование одиночки приводит к неявным зависимостям. Так как получение инстанции может происходить где угодно, это усложняет процесс анализа работы классов, использующих их.
Диаграмма
Пример
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
CLASS lcl_singleton DEFINITION CREATE PRIVATE. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_singleton) TYPE REF TO lcl_singleton. PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO lcl_singleton. ENDCLASS. CLASS lcl_singleton IMPLEMENTATION. METHOD get_instance. IF go_instance IS NOT BOUND. CREATE OBJECT go_instance. ENDIF. ro_singleton = go_instance. ENDMETHOD. ENDCLASS. START-OF-SELECTION. DATA: lo_singleton TYPE REF TO lcl_singleton. " Создание инстанции класса "LCL_SINGLETON" вне класса не разрешено " CREATE OBJECT lo_singleton. lo_singleton = lcl_singleton=>get_instance( ). |
Создание класса напрямую запрещено через CREATE PRIVATE, вместо этого создан метод возвращающий инстанцию класса, причем он проверяет, создан ли уже ранее объект или нет и если создан, возвращает его. Т.к. это статический атрибут, инстанция будет всегда одной в рамках сессии.
Одним из недостатков этого подхода является невозможность замены одиночки в юнит-тестах на объекты заглушки, решить эту проблему поможет несколько расширенная версия:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
INTERFACE lif_singleton. METHODS: do_something. ENDINTERFACE. CLASS lcl_singleton DEFINITION CREATE PRIVATE. PUBLIC SECTION. INTERFACES: lif_singleton. ALIASES: do_something FOR lif_singleton~do_something. CLASS-DATA: go_instance TYPE REF TO lif_singleton. CLASS-METHODS: get_instance RETURNING VALUE(ro_singleton) TYPE REF TO lif_singleton. ENDCLASS. CLASS lcl_singleton IMPLEMENTATION. METHOD get_instance. IF go_instance IS NOT BOUND. CREATE OBJECT go_instance TYPE lcl_singleton. ENDIF. ro_singleton = go_instance. ENDMETHOD. METHOD do_something. WRITE 'Something'. ENDMETHOD. ENDCLASS. CLASS lcl_singleton_for_testing DEFINITION. PUBLIC SECTION. INTERFACES: lif_singleton. ALIASES: do_something FOR lif_singleton~do_something. ENDCLASS. CLASS lcl_singleton_for_testing IMPLEMENTATION. METHOD do_something. WRITE 'Something for test'. ENDMETHOD. ENDCLASS. START-OF-SELECTION. DATA: lo_singleton TYPE REF TO lif_singleton. DATA: lo_singleton_for_testing TYPE REF TO lcl_singleton_for_testing. CREATE OBJECT lo_singleton_for_testing. lcl_singleton=>go_instance = lo_singleton_for_testing. lo_singleton ?= lcl_singleton=>get_instance( ). lo_singleton->do_something( ). |
Основная идея тут вынести поведение одиночки в интерфейс и через публичное свойство (или метод) подменить инстанцию на ту, что нужна для теста.
Кроме перечисленных подходов одиночка может быть представлен как класс с исключительно статическими методами и атрибутами.
Спасибо за статью!
Давно уже не писал ничего, думал всё.)
Не, точно не все 🙂
Я правильно понимаю, что речь идет о, например, классах-утилитах, у которых нет смысла получения экземпляра:
«Кроме перечисленных подходов одиночка может быть представлен как класс с исключительно статическими методами и атрибутами.»
Да, верно. Хотя тут скорее подойдет понятие «статический класс» https://m.habrahabr.ru/post/103681/
Одиночка всегда один. АУФ!