[English version of the article can be found here].
Цим постом я відкриваю цикл статей, присвячених паттернам проектування. Все написане мною грунтується виключно на особистому досвіді.
Патерни проектування - це опис деяких проблем, що виникають під час об'єктно-орієнтованого проектування, а також способів їх вирішення (як практичних, так і теоретичних). Іншими словами - це приклади правильних підходів до вирішення типових задач проектування.
Проста реалізація Singleton
Один з найпростіших способів реалізувати патерн Singleton на мові Java виглядає так:
Тепер приведу деякі пояснення з приводу реалізації шаблону.
Метод getInstance () створить рівно один екземпляр класу Singleton. Цей метод оголошений як synchronized. Зроблено це ось чому. В багатопоточних програмах при одночасному виклику методу getInstance () з декількох потоків можна створити кілька примірників класу Singleton. А повинен залишитися тільки один!
Від модифікатора synchronized можна позбутися. Для цього _instance потрібно проинициализировать:
а в методі getInstance () прибрати конструкцію "if". Тоді ініціалізація відбудеться під час завантаження класу.
Але використання пізньої ініціалізації (lazy initialization) краще в разі, якщо створення екземпляра класу займає багато часу. Та й у разі ледачою ініціалізації є можливість обробити виникли ісключітальние ситуації при виклику конструктора.
Сінглтон з double-checked locking
Проблема з lazy инициализацией залишається тільки в тому, що синхронізація потрібна по ідеї тільки один раз, щоб кілька потоків не увійшли в критичну секцію одночасно. Але після створення екземпляра класу від синхронізації хотілося б позбутися.
Найпоширеніший спосіб позбутися від зайвої синхронізації - це double-checked locking, який виглядає таким чином:
Сінглтон з Instance Holder
А ось ще один вартий уваги варіант реалізації шаблону Одинак з ледачою инициализацией:
Об'єкт буде ініціалізованим першим під час першого виклику методу getInstance (). Тобто ми перенесли проблему із синхронізацією на рівень завантажувача класів (class loader).
Але що якщо в додатку кілька Class Loader'ов або взагалі у нас розподілена система з декількома віртуальнмі машинами Java? Тоді все складно. Поговоримо про це іншим разом.
А взагалі зараз модно говорити, що реально крутий варіант патерну Одинак виглядає так:
Ось власне і все. Ах да! Навіщо нам це потрібно.
Практичний приклад Сінглтона
Мені доводиться найчастіше використовувати цей патерн при роботі з конфігурацією. Іноді конфігурацію програми зручно зберігати в файлі. Припустимо, це буде простий текстовий файл "props.txt" з рядками типу "ключ = значення". Нам потрібно гарантувати, що конфігурація в програмі буде в єдиному екземплярі. Другу ми б і так не створили, але потрібно заборонити це робити користувачеві класу. Отже,
Тепер для роботи з конфігурацією можна використовувати конструкцію виду:
Якщо імена властивостей в "props.txt" мінятися не будуть, можна описати їх в класі таким чином:
а значення отримувати так:
Патерн Singleton корисний не тільки при роботі з конфігураціями. Його можна використовувати також при написанні ConnectionPool, Factory і інших речей.
Ось тепер точно все.