{lang: ‘ru’}

ооп, программирование, шаблон, singleton, одиночкаНаверное, многие «объектно-ориентированные» программисты сталкивались с тем, что с ростом исходного кода становится все сложнее следить за единственностью экземпляра какого-либо класса. Для решения этой проблемы используется паттерн «Одиночка» или «Синглтон» (Singleton).

В каких ситуациях может пригодиться Синглтон? Ответ прост: в тех, где имеет место быть монопольное владение какой-либо информацией или состоянием. Для большей наглядности рассмотрим пример. Пусть у нас есть некоторая утилита, которая все свои настройки хранит в файле и при старте их загружает. При этом настройки можно менять в окне этой самой утилиты. Изменив их и нажав на кнопку «Save», вся конфигурация записывается в файл. И вот тут то назревает вопрос: как остальная часть программы узнает об изменении в настройках? Именно для этого в коде утилиты должен иметься единственный объект, который хранит в себе текущее состояние опций. И изменение настроек должно происходить через этот объект.

Проблема понятна, переходим к ее решению. Первое, что приходит в голову - создание глобальной переменной для хранения объекта нужного класса (Рис. 1). Казалось бы, во всей программе он будет единственным, но на самом деле этот прием проблему не решает.
Дело в том, что, во-первых, эту переменную можно переопределить. Любой может заново создать объект класса ClassSettings и заменить им текущий объект. Можно, конечно, перегрузить оператор присваивания, но это окажется бесполезным, если gSet представляет собой ссылку или указатель.
Во-вторых, разработчик может просто забыть о том, что переменная уже существует, а может даже и не знать об этом и создать новый объект с настройками. Такого рода проблема наиболее актуальна в проектах, над которыми работают несколько разработчиков.
В-третьих, наш объект будет вероятнее всего создаваться в самом начале работы программы. Это плохо тем, что не при всех сценариях происходит обращение к переменной. Так что, плюс ко всему, данный подход оказывается еще и нерациональным.

шаблон, singletone, одиночка

Рис. 1

Выбросив из головы идею о глобальной переменной, вспоминаем о существовании статических классов. Все методы и свойства объявляем как static, что гарантирует нам единственность и неповторимость (Рис. 2). Проблема со множеством копий действительно решена, а вот проблема с рациональностью нами вновь была проигнорирована. Статический класс создается слишком рано — во время запуска программы. Вдобавок мы получили еще один значительный минус: невозможность инициализации. Нет объекта — нет возможности инициализации. Можно, конечно, создать отдельный метод, который будет как бы «виртуальным инициализатором», но нет гарантии, что к методам нашего класса не обратятся раньше, чем «псевдоинициализация» выполнится.

шаблон, singletone, одиночка

Рис. 2

Убедившись в том, что две наши предыдущие догадки ошибочны, мы обращаемся к паттерну проектирования «Одиночка«. Сначала создадим класс с приватным конструктором (Рис. 3). Стоит напомнить, что приватные (private) методы и свойства доступны только внутри своего класса.

шаблон, singletone, одиночка

Рис. 3

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


Получайте новые статьи блога прямо себе на почту