singleton

Паттерны проектирования. Singleton.

Наши соц. сети: instagram, fb, tg

“Singleton — порождающий шаблон проектирования, гарантирующий, что в однопоточном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.” - Википедия.

Главной особенностью этого паттерна является то, что в каждом классе он создает только один объект. Кроме того, для класса создается единая точка входа. Обычно для этого используют метод доступа getInstance.

Можно сказать, что объектный адаптер (он же singleton) содержит экземпляр класса, который он переносит:

1

Singleton - это отдельный класс. У него есть собственный атрибут uniqueInstance, в котором хранится экземпляр класса Singleton. Конструктор класса является закрытым, и мы можем получить доступ к экземпляру только через метод доступа.

Если у нас уже есть экземпляр, то метод доступа отвечает за его возврат. В противном случае (то есть если экземпляра еще нет), наш метод будет отвечать также и за создание экземпляра.

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

Класс singleton может быть расширен наследованием. В таком случае клиенты должны иметь возможность использовать расширенные классы, не внося в него никаких изменений.

Чем полезен Singleton?

1)Singleton даёт нам возможность контролировать, как и когда клиенты будут обращаться к одноэлементному экземпляру. Вы контролируете доступ, потому что класс singleton инкапсулирует свой экземпляр.

2)Экономит системные ресурсы.

3)Этот шаблон проектирования обладает преимуществом перед глобальными переменными. Singleton позволяет избежать загрязнения пространства имен глобальными переменными.

4)Singleton упрощает код. При этом у нас повышается читабельность кода, что приводит к лучшему пониманию структуры кода. Благодаря всему этому код в конечном итоге гораздо легче протестировать.

Singleton в деле.

Смоделируем ситуацию: допустим у нас есть класс с именем DatabaseConnection. Этот класс определяет два атрибута: configuration и getUniqueIdentificator. Помимо этого DatabaseConnection еще и является подключением к нашей базе данных и используется несколькими клиентами (client1 и client2). Наглядная диаграмма нашей ситуации:

2

Код клиента выглядит следующим образом:

3

4

Каждый клиент создает новое соединение с базой данных и запрашивает уникальный идентификатор каждого из соединений. Одним из главных недостатков такой архитектуры является то, что нам приходится использовать больше ресурсов, чем это необходимо.

Так выглядит класс DatabaseConnection:

5

Мы видим, что в конфигурации базы данных доступен только закрытый атрибут, а доступ к уникальному идентификатору осуществляется с помощью атрибута public.

Взглянем теперь на пример кода, выполняющего это взаимодействие:

6

7

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

Решением является использование одноэлементного шаблона, который создает только один экземпляр класса. А теперь вот вам новая диаграмма с использованием паттерна Singleton:

8

Код, связанный с DatabaseConnection, теперь выглядит так:

9

Единственная точка доступа к экземпляру - статический метод getDatabaseConnection, который создает новый экземпляр в случае, когда экземпляр не существует, или, если экземпляр имеется, просто получит его. Таким образом, клиенты немного модифицируются, чтобы использовать этот экземпляр вместо создания своего собственного экземпляра:

10

Давайте взглянем на результат этих модификаций при выполнении программы:

11

Мы создали два сценария npm, которые запускают два примера, показанных здесь, после применения шаблона singleton.

npm run example1-problem
npm run example1-singleton-solution1

Давайте рассмотрим ещё один пример. Предположим, нам нужно использовать Singleton для нескольких классов. Допустим, набор героев, таких как Человек-паук и Бэтмен, являются одиночками. На следующей диаграмме вы можете увидеть эту ситуацию:

12

Код, связанный с клиентами, выглядит следующим образом:

13

14

Мы создаем наших героев, которые будут уникальными. Прежде всего, мы определим общий интерфейс для информации, которую будет содержать каждый из наших героев:

15

Наши герои уникальны, однако у них все же есть общие атрибуты и методы. Для этого мы определим родительский класс с именем HeroBase, который содержит общие черты как Spiderman, так и Batman. Это может выглядеть так:

16

И Бэтмен, и Человек-паук реализованы с помощью Singleton и хранят ссылку на единственный объект каждого класса. Эти классы следующие:

17

Наконец, пример кода для этого взаимодействия выглядит следующим образом:

18

А вот результат всех наших манипуляций:

19

Я создал скрипт npm, который запускает приведенный здесь пример после применения шаблона singleton.

npm run example2-singleton-solution1
 

Singleton антипаттерн?

Singleton полезен, так как он помогает избежать некоторых сложностей в разработке проекта. Он позволяет контролировать создание экземпляров классов в одной точке доступной клиентам. Благодаря этому паттерну проектирования вы сможете сэкономить системные ресурсы. Тем не менее необходимо знать, что этот паттерн имеет очень плохую репутацию. Он считается даже антипаттерном, потому что он создает глобальные переменные, которые могут быть доступны и изменены из любого места кода.

Любой паттерн - это определённая схема или шаблон. И этот шаблон может быть простым в реализации. Однако важно помнить, что здесь есть множество нюансов. Так что если вы хотите использовать паттерны проектирования наиболее продуктивно, то помимо реализации этих паттернов необходимо тщательнейшим образом разобраться в основных принципах их работы. И тогда эти самые паттерны во много раз облегчат вам жизнь!