Шаблон проектирования синглтона
Одноэлементный шаблон - это шаблон проектирования программного обеспечения, который ограничивает создание класса одним «единственным» экземпляром. Это полезно, когда нужен ровно один объект для координации действий в системе. Термин происходит от математической концепции синглтона.
Критики считают синглтон анти-шаблоном в том смысле, что он часто используется в сценариях, где он не выгоден, вводит ненужные ограничения в ситуациях, когда единственный экземпляр класса фактически не требуется, и вводит глобальное состояние в приложение.
Что такое синглтон?
Синглтон - это шаблон проектирования, который позволяет гарантировать, что у класса есть только один экземпляр, обеспечивая при этом глобальную точку доступа к этому экземпляру. Шаблон синглтона решает две проблемы одновременно, нарушая единую ответственность Принцип:
- Убедитесь, что в классе есть только один экземпляр:
Наиболее частой причиной этого является контроль доступа к какому-либо общему ресурсу, например, базе данных или файлу. Обратите внимание, что такое поведение невозможно реализовать с помощью обычного конструктора, поскольку вызов конструктора должен всегда возвращать новый объект по дизайну.
- Предоставьте этому экземпляру глобальную точку доступа.
Как и глобальная переменная, шаблон Singleton позволяет вам получить доступ к какому-либо объекту из любой точки программы. Однако он также защищает этот экземпляр от перезаписи другим кодом.
В настоящее время шаблон синглтона стал настолько популярным, что люди могут называть что-то синглтоном, даже если он решает только одну из перечисленных проблем.
Используйте шаблон Singleton:
- Должен быть один и только один экземпляр класса
- Класс должен быть доступен клиентам
- Класс не должен требовать параметров как часть своей конструкции.
- Когда создание экземпляра обходится дорого, синглтон может улучшить производительность.
Решение
Все реализации синглтона имеют эти два общих шага:
- Сделайте конструктор по умолчанию закрытым, чтобы предотвратить другие объекты
- от использования оператора new с классом Singleton.
- Создайте статический метод создания, который действует как конструктор. Под капотом этот метод вызывает частный конструктор для создания объекта и сохраняет его в статическом поле. Все последующие вызовы этого метода возвращают кэшированный объект.
Проблема: Регистратор
у нас есть простой регистратор, в котором мы сохраняем сообщение и дату как журнал в нашем файле журнала.
//typescript class Logger { logs: any constructor() { this.logs = []; } get_log() { // return all logs return this.logs; } set_log(message: string) { // save message to logger // it can be file or database this.logs.push(message) } } class LoggerSingleton { static instance: Logger; constructor() { if (!LogSingleton.instance) { LogSingleton.instance = new Logger; } } get_instance() { return LogSingleton.instance } } // this line always call the same instance of Logger let logger = new LogSingleton().get_instance() logger.set_log('Example Massage') logger.get_log()
в этом примере «LoggerSingleton» устанавливает экземпляр «Logger», и тогда у нас везде есть только один экземпляр нашего Logger.
Синглтон в Node.js
в Node.js просто добавьте `module.exports` с новым экземпляром класса, а затем автоматически создайте только один экземпляр в нашей программе.
class Logger { constructor() { this.logs = []; } get_log() { // return all logs return this.logs; } set_log(message: string) { // save message to logger // it can be file or database this.logs.push(message) } } module.exports = new Logger()
Проблема: подключение к базе данных
мы хотим один раз подключиться к базе данных в нашем приложении и использовать ее. это означает, что когда мы хотим использовать экземпляр базы данных, мы просто вызываем тот же экземпляр.
class Database { // The field for storing the singleton instance should be // declared static. private static instance: Database // The singleton's constructor should always be private to // prevent direct construction calls with the `new` // operator. private constructor(){ } // The static method that controls access to the singleton //instance public static getInstance() { if (this.instance == null) { this.instance = new Database() } return this.instance } // Finally, any singleton should define some business logic // which can be executed on its instance. public query(sql) { // For instance, all database queries of an app go // through this method. Therefore, you can place // throttling or caching logic here. // ... } } class Application { main() { Database foo = Database.getInstance() foo.query("SELECT ...") // ... Database bar = Database.getInstance() bar.query("SELECT ...") // The variable `bar` will contain the same object as // the variable `foo`. } }
Плюсы
- Вы можете быть уверены, что у класса есть только один экземпляр.
- Вы получаете глобальную точку доступа к этому экземпляру.
- Одноэлементный объект инициализируется только при первом запросе.
Минусы
- Нарушает Принцип единой ответственности. Шаблон решает сразу две проблемы.
- Шаблон Singleton может маскировать плохой дизайн, например, когда компоненты программы слишком много знают друг о друге.
- Этот шаблон требует особой обработки в многопоточной среде, чтобы несколько потоков не создавали одноэлементный объект несколько раз.
- Модульное тестирование клиентского кода Singleton может оказаться трудным, поскольку многие тестовые среды полагаются на наследование при создании фиктивных объектов. Поскольку конструктор одноэлементного класса является частным и переопределение статических методов невозможно на большинстве языков, вам нужно будет подумать о творческом способе имитации одноэлементного класса. Или просто тесты не пишите. Или не используйте шаблон Singleton.