Шаблон одноэлементного проектирования — это шаблон проектирования программного обеспечения, который ограничивает создание экземпляров класса одним объектом.
По сравнению с другими шаблонами творческого проектирования, такими как абстрактная фабрика , фабрика и шаблон строителя, синглтон будет создавать объект, но также будет нести ответственность за существование только одного экземпляра этого объекта.
При создании класса как одиночного есть некоторые определенные проблемы, которые необходимо решить.
- Как можно гарантировать, что у класса есть только один экземпляр.
- Как можно легко получить доступ к единственному экземпляру класса?
- Как класс может контролировать свою реализацию
- Как можно ограничить количество экземпляров класса
Предположим, у нас есть класс, который отправляет сообщения.
Класс Messenger.
1
2
3
4
5
6
7
8
|
package com.gkatzioura.design.creational.singleton; public class Messenger { public void send(String message) { } } |
Однако мы хотим, чтобы процедура сообщения была обработана только одним экземпляром класса Messenger. Представьте себе сценарий, в котором класс Messenger открывает tcp-соединение (например, xmpp) и должен поддерживать соединение в активном состоянии для отправки сообщений. Будет довольно неэффективно открывать новое соединение xmpp каждый раз, когда нам нужно будет отправить сообщение.
Поэтому мы продолжим и сделаем класс посланника синглтоном.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
package com.gkatzioura.design.creational.singleton; public class Messenger { private static Messenger messenger = new Messenger(); private Messenger() {} public static Messenger getInstance() { return messenger; } public void send(String message) { } } |
Как видите, мы установили конструктор мессенджера как приватный и инициализировали мессенджер, используя статическую переменную.
Статические переменные являются переменными уровня класса, распределение памяти происходит только один раз, когда класс загружается в память. Таким образом мы гарантируем, что класс мессенджера будет создан только один раз. Метод getInstance будет извлекать экземпляр статического мессенджера после его вызова.
Очевидно, что предыдущий подход имеет свои плюсы и минусы. Нам не нужно беспокоиться о безопасности потоков, и экземпляр будет создан только тогда, когда будет загружен класс Messenger. Однако ему не хватает гибкости. Рассмотрим сценарий передачи переменных конфигурации в конструктор Messenger. Это невозможно при использовании предыдущего подхода.
Обходной путь должен создать экземпляр класса мессенджера в методе getInstance.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.gkatzioura.design.creational.singleton.lait; public class Messenger { private static Messenger messenger; private Messenger() {} public static Messenger getInstance() { if (messenger== null ) { messenger = new Messenger(); } return messenger; } public void send(String message) { } } |
Вышеупомянутый подход может работать в определенном случае, но он пропускает безопасность потока в случаях, когда класс может быть создан в многопоточной среде.
Самый простой подход к обеспечению безопасности потока нашего класса — это синхронизировать метод getInstance.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.gkatzioura.design.creational.singleton.lait; public class Messenger { private static Messenger messenger; private Messenger() {} public synchronized static Messenger getInstance() { if (messenger== null ) { messenger = new Messenger(); } return messenger; } public void send(String message) { } } |
Это будет работать. По крайней мере, создание мессенджера будет синхронизировано и дубликаты не будут созданы. Проблема этого подхода заключается в том, что синхронизация необходима только один раз при создании объекта. Использование приведенного выше кода приведет к ненужным накладным расходам.
Другой подход заключается в использовании метода двойной проверки блокировки. Теперь блокировка с двойной проверкой требует особой осторожности, так как легко выбрать неправильную реализацию вместо правильной.
Наилучшим подходом является реализация отложенной загрузки с использованием ключевого слова volatile .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.gkatzioura.design.creational.singleton.dcl; public class Messenger { private static final Object lock = new Object(); private static volatile Messenger messenger; private Messenger() {} public static Messenger getInstance() { if (messenger== null ) { synchronized (lock) { if (messenger== null ) { messenger = new Messenger(); } } } return messenger; } public void send(String message) { } } |
Используя ключевое слово volatile, мы предотвращаем изменение порядка записи volatile по отношению к любому предыдущему чтению или записи, а также чтение volatile, которое необходимо изменить по отношению к любому последующему чтению или записи. Также объект взаимного исключения используется для достижения синхронизации.
Подводя итог, мы создали объект, и мы также убедились, что будет только один экземпляр этого объекта. Также мы позаботились о том, чтобы не возникало проблем при создании объекта в многопоточной среде.
Вы можете найти исходный код на github .
В следующем посте мы рассмотрим образец прототипа .
Также я собрал шпаргалку, содержащую сводку шаблонов креационного дизайна. Зарегистрируйтесь в ссылке, чтобы получить его.
Опубликовано на Java Code Geeks с разрешения Эммануила Гкациоураса, партнера нашей программы JCG. Смотрите оригинальную статью здесь: Шаблоны креационного дизайна: шаблон Singleton
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |