О паттерне проектирования Singleton банды четырёх уже сказано много всяких гадостей. О разных нарушаемых Singleton'ом принципах можно почитать, например, здесь. И, похоже, мне есть что добавить.
Первопричина всех бед с GoF Singleton'ом, в том, что для подавляющего большинства классов «Singleton'овость» – это деталь их реализации. Просто эти классы так удобнее реализовать, если вся система будет работать с одним единственным объектом каждого. GoF советует эту деталь реализации для всех Singleton'ов выносить наружу, в виде метода getInstance().
А что если возникнет необходимость реализовать класс по-другому? Начнутся проблемы. Если новая реализация больше не будет Singleton'ной, то придётся менять не только сам класс но и всех его клиентов, убирая все вызовы getInstance() и продумывая стратегию создания экземпляров для каждого случая заново.
А существует ли вообще способ спрятать это свойство «Singleton'овости» класса от его клиентов, вместе со всеми проблемами и нарушаемыми принципами? Существует. Это можно сделать при помощи архитектурного паттерна Dependency Injection/Inversion of Control (IoC). IoC контейнер (IoCC) скрывает «Singleton' овость» класса от его клиентов и забирает себе всю ответственность по управлению зависимостями, освобождая Singleton от: «… и предоставляет к нему глобальную точку доступа...» из определения банды четырёх. Глобальная точка доступа больше не нужна, клиенты Singleton'а больше не знают, сколько именно экземпляров класса-зависимости создаёт для них IoCC. Например, мой любимый IoCC Google Guice позволяет пометить класс аннотацией Singleton, но самое главное: он позволяет убрать эту аннотацию в любой момент без необходимости менять клиентов класса. Под контролем IoCC «Singleton'овость» всегда остаётся деталью реализации класса, помеченного как Singleton.
Существуют и исключения, когда «Singleton'овость» действительно должна быть свойством интерфейса класса, а не деталью его реализации. Одно из таких исключений: Flyweight Singleton. Например, мы пишем классы для абстрактного синтаксического дерева языка SQL, и одним из таких классов будет SqlNull. Желательно, что бы у этого класса был один единственный объект, чтобы все об этом знали, и сравнивали этот объект с остальными только по ссылке. В этом случае GoF Singleton нам подходит отлично.
Итак. Для классов-сервисов, т.е. для большинства случаев, «Singleton'овость» – это деталь реализации и спрятать её позволяет IoCC; для Flyweight Singleton'ов – это часть интерфейса, и выставить её наружу можно как предлагают GoF при помощи getInstance().
Первопричина всех бед с GoF Singleton'ом, в том, что для подавляющего большинства классов «Singleton'овость» – это деталь их реализации. Просто эти классы так удобнее реализовать, если вся система будет работать с одним единственным объектом каждого. GoF советует эту деталь реализации для всех Singleton'ов выносить наружу, в виде метода getInstance().
А что если возникнет необходимость реализовать класс по-другому? Начнутся проблемы. Если новая реализация больше не будет Singleton'ной, то придётся менять не только сам класс но и всех его клиентов, убирая все вызовы getInstance() и продумывая стратегию создания экземпляров для каждого случая заново.
А существует ли вообще способ спрятать это свойство «Singleton'овости» класса от его клиентов, вместе со всеми проблемами и нарушаемыми принципами? Существует. Это можно сделать при помощи архитектурного паттерна Dependency Injection/Inversion of Control (IoC). IoC контейнер (IoCC) скрывает «Singleton' овость» класса от его клиентов и забирает себе всю ответственность по управлению зависимостями, освобождая Singleton от: «… и предоставляет к нему глобальную точку доступа...» из определения банды четырёх. Глобальная точка доступа больше не нужна, клиенты Singleton'а больше не знают, сколько именно экземпляров класса-зависимости создаёт для них IoCC. Например, мой любимый IoCC Google Guice позволяет пометить класс аннотацией Singleton, но самое главное: он позволяет убрать эту аннотацию в любой момент без необходимости менять клиентов класса. Под контролем IoCC «Singleton'овость» всегда остаётся деталью реализации класса, помеченного как Singleton.
Существуют и исключения, когда «Singleton'овость» действительно должна быть свойством интерфейса класса, а не деталью его реализации. Одно из таких исключений: Flyweight Singleton. Например, мы пишем классы для абстрактного синтаксического дерева языка SQL, и одним из таких классов будет SqlNull. Желательно, что бы у этого класса был один единственный объект, чтобы все об этом знали, и сравнивали этот объект с остальными только по ссылке. В этом случае GoF Singleton нам подходит отлично.
Итак. Для классов-сервисов, т.е. для большинства случаев, «Singleton'овость» – это деталь реализации и спрятать её позволяет IoCC; для Flyweight Singleton'ов – это часть интерфейса, и выставить её наружу можно как предлагают GoF при помощи getInstance().