Использование паттерна синглтон. Паттерн Одиночка (Singleton) - описание (шаблон) Что такое статический класс

22.10.2015
23:12

Назначение паттерна Синглтон (или Одиночка) заключается в обеспечении доступа к некоторому уникальному объекту из любой точки приложения. Под уникальностью подразумевается, что такой объект существует в памяти в единственном экземпляре и другие экземпляры созданы быть не могут.

Без условия уникальности Синглтон является обычной глобальной переменной с учетом вытекающих плюсов и минусов (которых большинство). Поэтому перед тем, как использовать этот паттерн, убедитесь, что он действительно подходит. Объект, который предполагается реализовать в виде Синглтона должен быть по-настоящему единственным на уровне системы.

Пример реализация паттерна Singleton на C++

Технически реализовать объект-Синглтон и использовать его в приложении довольно просто (что подкупает):

#include class MySingleton { public: // Функция-член для доступа к единственному экземпляру static MySingleton* getInstance() { static MySingleton instance; return &instance; } // Наполняем полезным функционалом, как и любой другой класс void test() { std::cout << "Singleton test" << std::endl; } private: // Объявляем конструктор закрытым, чтобы нельзя было // создавать экземпляры класса извне MySingleton() { } }; int main() { // Используем Синглтон MySingleton::getInstance()->test(); // А это работать не будет, поскольку конструктор - закрытый // MySingleton singleton; return 0; }

При желании объект-Синглтон можно адаптировать к многопоточной среде выполнения с помощью мьютексов.

Реклама

Всегда думайте перед созданием Синглтона

Заманчиво иметь некий объект, доступный в любой точке программы. Но это нарушает многие принципы создания хорошего кода. Поэтому не спешите добавлять Синглтоны, которые усложняют логику программы и вносят лишние зависимости.

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

Однако в этом случае появляется серьезная проблема. Все компоненты начинают зависеть от Синглтона. Если понадобится перенести только один из классов в другое приложение, то придется тащить вместе с ним и Синглтон, который может быть предназначен для управления параметрами десятков других классов. Лучше потратить немного больше времени на проектирование, но обеспечить четкую передачу параметров в классы через их конструкторы, а не через незаметный Синглтон.

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

Хотя есть и вполне безобидные применения Синглтонов. Например, при реализации другого паттерна: Абстрактная Фабрика.

Также использование Синглтона оправдано для представления физически уникальных ресурсов компьютера. Например, систему слежения за подключением/отключением USB-устройств уместно реализовать в виде Синглтона.

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

Проблема

Одиночка решает сразу две проблемы, нарушая принцип единственной ответственности класса.

    Гарантирует наличие единственного экземпляра класса . Чаще всего это полезно для доступа к какому-то общему ресурсу, например, базе данных.

    Представьте, что вы создали объект, а через некоторое время пробуете создать ещё один. В этом случае хотелось бы получить старый объект, вместо создания нового.

    Такое поведение невозможно реализовать с помощью обычного конструктора, так как конструктор класса всегда возвращает новый объект.


Клиенты могут не подозревать, что работают с одним и тем же объектом.

    Предоставляет глобальную точку доступа . Это не просто глобальная переменная, через которую можно достучаться к определённому объекту. Глобальные переменные не защищены от записи, поэтому любой код может подменять их значения без вашего ведома.

    Но есть и другой нюанс. Неплохо бы хранить в одном месте и код, который решает проблему №1, а также иметь к нему простой и доступный интерфейс.

Интересно, что в наше время паттерн стал настолько известен, что теперь люди называют «одиночками» даже те классы, которые решают лишь одну из проблем, перечисленных выше.

Решение

Все реализации одиночки сводятся к тому, чтобы скрыть конструктор по умолчанию и создать публичный статический метод, который и будет контролировать жизненный цикл объекта-одиночки.

Если у вас есть доступ к классу одиночки, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его ни вызвали, он всегда будет отдавать один и тот же объект.

Аналогия из жизни

Правительство государства - хороший пример одиночки. В государстве может быть только одно официальное правительство. Вне зависимости от того, кто конкретно заседает в правительстве, оно имеет глобальную точку доступа «Правительство страны N».

Структура



    Одиночка определяет статический метод getInstance , который возвращает единственный экземпляр своего класса.

    Конструктор одиночки должен быть скрыт от клиентов. Вызов метода getInstance должен стать единственным способом получить объект этого класса.

Псевдокод

В этом примере роль Одиночки отыгрывает класс подключения к базе данных.

Этот класс не имеет публичного конструктора, поэтому единственный способ получить его объект - это вызвать метод getInstance . Этот метод сохранит первый созданный объект и будет возвращать его при всех последующих вызовах.

// Класс одиночки определяет статический метод `getInstance`, // который позволяет клиентам повторно использовать одно и то же // подключение к базе данных по всей программе. class Database is // Поле для хранения объекта-одиночки должно быть объявлено // статичным. private static field instance: Database // Конструктор одиночки всегда должен оставаться приватным, // чтобы клиенты не могли самостоятельно создавать // экземпляры этого класса через оператор `new`. private constructor Database() is // Здесь может жить код инициализации подключения к // серверу баз данных. // ... // Основной статический метод одиночки служит альтернативой // конструктору и является точкой доступа к экземпляру этого // класса. public static method getInstance() is if (Database.instance == null) then acquireThreadLock() and then // На всякий случай ещё раз проверим, не был ли // объект создан другим потоком, пока текущий // ждал освобождения блокировки. if (Database.instance == null) then Database.instance = new Database() return Database.instance // Наконец, любой класс одиночки должен иметь какую-то // полезную функциональность, которую клиенты будут // запускать через полученный объект одиночки. public method query(sql) is // Все запросы к базе данных будут проходить через этот // метод. Поэтому имеет смысл поместить сюда какую-то // логику кеширования. // ... class Application is method main() is Database foo = Database.getInstance() foo.query("SELECT ...") // ... Database bar = Database.getInstance() bar.query("SELECT ...") // Переменная "bar" содержит тот же объект, что и // переменная "foo".

Применимость

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

Одиночка скрывает от клиентов все способы создания нового объекта, кроме специального метода. Этот метод либо создаёт объект, либо отдаёт существующий объект, если он уже был создан.

Когда вам хочется иметь больше контроля над глобальными переменными.

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

Тем не менее, в любой момент вы можете расширить это ограничение и позволить любое количество объектов-одиночек, поменяв код в одном месте (метод getInstance).

Шаги реализации

    Добавьте в класс приватное статическое поле, которое будет содержать одиночный объект.

    Объявите статический создающий метод, который будет использоваться для получения одиночки.

Подводные камни Singleton: почему самый известный шаблон проектирования нужно использовать с осторожностью

Паттерн - пожалуй, самый известный паттерн проектирования. Тем не менее, он не лишен недостатков, поэтому некоторые программисты (например, Егор Бугаенко) считают его антипаттерном. Разбираемся в том, какие же подводные камни таятся в Singleton’е.

Определение паттерна

Само описание паттерна достаточно простое - класс должен гарантированно иметь лишь один объект, и к этому объекту должен быть предоставлен глобальный доступ. Скорее всего, причина его популярности как раз и кроется в этой простоте - всего лишь один класс, ничего сложного. Это, наверное, самый простой для изучения и реализации паттерн. Если вы встретите человека, который только что узнал о существовании паттернов проектирования, можете быть уверены, что он уже знает про Singleton. Проблема заключается в том, что когда из инструментов у вас есть только молоток, всё вокруг выглядит как гвозди. Из-за этого “Одиночкой” часто злоупотребляют.

Простейшая реализация

Как уже говорилось выше, в этом нет ничего сложного:

  • Сделайте конструктор класса приватным, чтобы не было возможности создать экземпляр класса извне.
  • Храните экземпляр класса в private static поле.
  • Предоставьте метод, который будет давать доступ к этому объекту.
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }

Принцип единственной обязанности

В объектно-ориентированном программировании существует правило хорошего тона - “Принцип едиственной обязанности” (Single Responsibility Principle, первая буква в аббревиатуре SOLID). Согласно этому правилу, каждый класс должен отвечать лишь за один какой-то аспект. Совершенно очевидно, что любой Singleton-класс отвечает сразу за две вещи: за то, что класс имеет лишь один объект, и за реализацию того, для чего этот класс вообще был создан.

Принцип единственной обязанности был создан не просто так - если класс отвечает за несколько действий, то, внося изменения в один аспект поведения класса, можно затронуть и другой, что может сильно усложнить разработку. Так же разработку усложняет тот факт, что переиспользование (reusability) класса практически невозможно. Поэтому хорошим шагом было бы, во-первых, вынести отслеживание того, является ли экземпляр класса единственным, из класса куда-либо во вне, а во-вторых, сделать так, чтобы у класса, в зависимости от контекста, появилась возможность перестать быть Singleton’ом, что позволило бы использовать его в разных ситуациях, в зависимости от необходимости (т.е. с одним экземпляром, с неограниченным количество экземпляров, с ограниченным набором экземпляров и так далее).

Тестирование

Один из главных минусов паттерна “Одиночка” - он сильно затрудняет юнит-тестирование. “Одиночка” привносит в программу глобальное состояние, поэтому вы не можете просто взять и изолировать классы, которые полагаются на Singelton. Поэтому, если вы хотите протестировать какой-то класс, то вы обязаны вместе с ним тестировать и Singleton, но это ещё полбеды. Состояние “Одиночки” может меняться, что порождает следующие проблемы:

  • Порядок тестов теперь имеет значение;
  • Тесты могут иметь нежелательные сторонние эффекты, порождённые Singleton’ом;
  • Вы не можете запускать несколько тестов параллельно;
  • Несколько вызовов одного и того же теста могут приводить к разным результатам.

На эту тему есть отличный доклад с “Google Tech Talks”:

Скрытые зависимости

Обычно, если классу нужно что-то для работы, это сразу понятно из его методов и конструкторов. Когда очевидно, какие зависимости есть у класса, гораздо проще их предоставить. Более того, в таком случае вы можете использовать вместо реально необходимых зависимостей заглушки для тестирования. Если же класс использует Singleton, это может быть совершенно не очевидно. Всё становится гораздо хуже, если экземпляру класса для работы необходима определённая инициализация (например, вызов метода init(...) или вроде того). Ещё хуже, если у вас существует несколько Singleton’ов, которые должны быть созданы и инициализированы в определённом порядке.

Загрузчик класса

Если говорить о Java, то обеспечение существования лишь одного экземпляра класса, которое так необходимо для Singleton, становится всё сложнее. Проблема в том, что классическая реализация не проверяет, существует ли один экземпляр на JVM, он лишь удостоверяется, что существует один экземпляр на classloader. Если вы пишете небольшое клиентское приложение, в котором используется лишь один classloader, то никаких проблем не возникнет. Однако если вы используете несколько загрузчиков класса или ваше приложение должно работать на сервере (где может быть запущено несколько экземпляров приложения в разных загрузчиках классов), то всё становится очень печально.

Десериализация

Ещё один интересный момент заключается в том, что на самом деле стандартная реализация Singleton не запрещает создавать новые объекты. Она запрещает создавать новые объекты через конструктор . А ведь существуют и другие способы создать экземпляр класса, и один из них - сериализация и десериализация. Полной защиты от намеренного создания второго экземпляра Singelton’а можно добиться только с помощью использования enum’а с единственным состоянием, но это - неоправданное злоупотребление возможностями языка, ведь очевидно, что enum был придуман не для этого.

Потоконебезопасность

Один из популярных вариантов реализации Singleton содержит ленивую инициализацию. Это значит, что объект класса создаётся не в самом начале, а лишь когда будет получено первое обращение к нему. Добиться этого совсем не сложно:

Public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }

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

  • Первый поток обращается к getInstance() , когда объект ещё не создан;
  • В это время второй тоже обращается к этому методу, пока первый ещё не успел создать объект, и сам создаёт его;
  • Первый поток создаёт ещё один, второй, экземпляр класса.

Разумеется, можно просто пометить метод как synchronised , и эта проблема исчезнет. Проблема заключается в том, что, сохраняя время на старте программы, мы теперь будем терять его каждый раз при обращении к Singleton’у из-за того, что метод синхронизирован, а это очень дорого, если к экземпляру приходится часто обращаться. А ведь единственный раз, когда свойство synchronised действительно требуется - первое обращение к методу.

Есть два способа решить эту проблему. Первый - пометить как synchronised не весь метод, а только блок, где создаётся объект:

Public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }

Не забывайте, что это нельзя использовать в версии Java ниже, чем 1.5, потому что там используется иная модель памяти. Также не забудьте пометить поле instance как volatile .

Второй путь - использовать паттерн “Lazy Initialization Holder”. Это решение основано на том, что вложенные классы не инициализируются до первого их использования (как раз то, что нам нужно):

Public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } private static class SingletonHolder { private static final Singleton instance = new Singleton(); } }

Рефлексия

Мы запрещаем создавать несколько экземпляров класса, помечая конструктор приватным. Тем не менее, используя рефлексию, можно без особого труда изменить видимость конструктора с private на public прямо во время исполнения:

Class clazz = Singleton.class; Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true);

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

Заключение

Несмотря на то, что паттерн Singleton очень известный и популярный, у него есть множество серьёзных недостатков. Чем дальше, тем больше этих недостатков выявляется, и оригинальные паттерны из книги GOF “Design Patterns” часто сегодня считаются антипаттернами. Тем не менее, сама идея иметь лишь один объект на класс по-прежнему имеет смысл, но достаточно сложно реализовать ее правильно.

Последнее обновление: 23.12.2018

Одиночка (Singleton, Синглтон) - порождающий паттерн, который гарантирует, что для определенного класса будет создан только один объект, а также предоставит к этому объекту точку доступа.

Когда надо использовать Синглтон? Когда необходимо, чтобы для класса существовал только один экземпляр

Синглтон позволяет создать объект только при его необходимости. Если объект не нужен, то он не будет создан. В этом отличие синглтона от глобальных переменных.

Классическая реализация данного шаблона проектирования на C# выглядит следующим образом:

Class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } }

В классе определяется статическая переменная - ссылка на конкретный экземпляр данного объекта и приватный конструктор. В статическом методе getInstance() этот конструктор вызывается для создания объекта, если, конечно, объект отсутствует и равен null.

Для применения паттерна Одиночка создадим небольшую программу. Например, на каждом компьютере можно одномоментно запустить только одну операционную систему. В этом плане операционная система будет реализоваться через паттерн синглтон:

Class Program { static void Main(string args) { Computer comp = new Computer(); comp.Launch("Windows 8.1"); Console.WriteLine(comp.OS.Name); // у нас не получится изменить ОС, так как объект уже создан comp.OS = OS.getInstance("Windows 10"); Console.WriteLine(comp.OS.Name); Console.ReadLine(); } } class Computer { public OS OS { get; set; } public void Launch(string osName) { OS = OS.getInstance(osName); } } class OS { private static OS instance; public string Name { get; private set; } protected OS(string name) { this.Name=name; } public static OS getInstance(string name) { if (instance == null) instance = new OS(name); return instance; } }

Синглтон и многопоточность

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

Static void Main(string args) { (new Thread(() => { Computer comp2 = new Computer(); comp2.OS = OS.getInstance("Windows 10"); Console.WriteLine(comp2.OS.Name); })).Start(); Computer comp = new Computer(); comp.Launch("Windows 8.1"); Console.WriteLine(comp.OS.Name); Console.ReadLine(); }

Здесь запускается дополнительный поток, который получает доступ к синглтону. Параллельно выполняется тот код, который идет запуска потока и кторый также обращается к синглтону. Таким образом, и главный, и дополнительный поток пытаются инициализровать синглтон нужным значением - "Windows 10", либо "Windows 8.1". Какое значение сиглтон получит в итоге, пресказать в данном случае невозможно.

Вывод программы может быть такой:

Windows 8.1 Windows 10

Или такой:

Windows 8.1 Windows 8.1

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

If (instance == null) instance = new OS(name);

Чтобы решить эту проблему, перепишем класс синглтона следующим образом:

Class OS { private static OS instance; public string Name { get; private set; } private static object syncRoot = new Object(); protected OS(string name) { this.Name = name; } public static OS getInstance(string name) { if (instance == null) { lock (syncRoot) { if (instance == null) instance = new OS(name); } } return instance; } }

Чтобы избежать одновременного доступа к коду из разных потоков критическая секция заключается в блок lock .

Другие реализации синглтона

Выше были рассмотрены общие стандартные реализации: потоконебезопасная и потокобезопасная реализации паттерна. Но есть еще ряд дополнительных реализаций, которые можно рассмотреть.

Потокобезопасная реализация без использования lock

public class Singleton { private static readonly Singleton instance = new Singleton(); public string Date { get; private set; } private Singleton() { Date = System.DateTime.Now.TimeOfDay.ToString(); } public static Singleton GetInstance() { return instance; } }

Данная реализация также потокобезопасная, то есть мы можем использовать ее в потоках так:

(new Thread(() => { Singleton singleton1 = Singleton.GetInstance(); Console.WriteLine(singleton1.Date); })).Start(); Singleton singleton2 = Singleton.GetInstance(); Console.WriteLine(singleton2.Date);

Lazy-реализация

Определение объекта синглтона в виде статического поля класса открывает нам дорогу к созданию Lazy-реализации паттерна Синглтон, то есть такой реализации, где данные будут инициализироваться только перед непосредственным использованием. Поскольку статические поля инициализируются перед первым доступом к статическому членам класса и перед вызовом статического конструктора (при его наличии). Однако здесь мы можем столкнуться с двумя трудностями.

Во-первых, класс синглтона может иметь множество статических переменных. Возможно, мы вообще не будем обращаться к объекту синглтона, а будем использовать какие-то другие статические переменные:

Public class Singleton { private static readonly Singleton instance = new Singleton(); public static string text = "hello"; public string Date { get; private set; } private Singleton() { Console.WriteLine($"Singleton ctor {DateTime.Now.TimeOfDay}"); Date = System.DateTime.Now.TimeOfDay.ToString(); } public static Singleton GetInstance() { Console.WriteLine($"GetInstance {DateTime.Now.TimeOfDay}"); Thread.Sleep(500); return instance; } } class Program { static void Main(string args) { Console.WriteLine($"Main {DateTime.Now.TimeOfDay}"); Console.WriteLine(Singleton.text); Console.Read(); } }

В данном случае идет только обращение к переменной text, однако статическое поле instance также будет инициализировано. Например, консольный вывод в данном случае мог бы выглядеть следующим образом:

Singleton ctor 16:05:54.1469982 Main 16:05:54.2920316 hello

В данном случае мы видим, что статическое поле instance инициализировано.

Для решения этой проблемы выделим отдельный внутренний класс в рамках класса синглтона:

Public class Singleton { public string Date { get; private set; } public static string text = "hello"; private Singleton() { Console.WriteLine($"Singleton ctor {DateTime.Now.TimeOfDay}"); Date = DateTime.Now.TimeOfDay.ToString(); } public static Singleton GetInstance() { Console.WriteLine($"GetInstance {DateTime.Now.TimeOfDay}"); return Nested.instance; } private class Nested { internal static readonly Singleton instance = new Singleton(); } } class Program { static void Main(string args) { Console.WriteLine($"Main {DateTime.Now.TimeOfDay}"); Console.WriteLine(Singleton.text); Console.Read(); } }

Теперь статическая переменная, которая представляет объект синглтона, определена во вложенном классе Nested. Чтобы к этой переменной можно было обращаться из класса синглтона, она имеет модификатор internal, в то же время сам класс Nested имеет модификатор private, что позволяет гарантировать, что данный класс будет доступен только из класса Singleton.

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

Main 16:11:40.1320873 hello

Далее мы сталкиваемся со второй проблемой: статические поля инициализируются перед первым доступом к статическому членам класса и перед вызовом статического конструктора (при его наличии). Но когда именно? Если класс содержит статические поля, не содержит статического конструктора, то время инициализации статических полей зависит от реализации платформы. Нередко это непосредственно перед первым использованием, но тем не менее момент точно не определен - это может быть происходить и чуть раньше. Однако если класс содержит статический конструктор, то статические поля будут инициализироваться непосредственно либо при создании первого экземпляра класса, либо при первом обращении к статическим членам класса.

Например, рассмотрим выполнение следующей программы:

Static void Main(string args) { Console.WriteLine($"Main {DateTime.Now.TimeOfDay}"); Console.WriteLine(Singleton.text); Singleton singleton1 = Singleton.GetInstance(); Console.WriteLine(singleton1.Date); Console.Read(); }

Ее возможный консольный вывод:

Main 16:33:33.1404818 hello Singleton ctor 16:33:33.1564802 GetInstance 16:33:33.1574824 16:33:33.1564802

Мы видим, что код метода GetInstance, который идет до вызова конструктора класса Singleton, выполняется после выполнения этого конструктора. Поэтому добавим в выше определенный класс Nested статический конструктор:

Public class Singleton { public string Date { get; private set; } public static string text = "hello"; private Singleton() { Console.WriteLine($"Singleton ctor {DateTime.Now.TimeOfDay}"); Date = DateTime.Now.TimeOfDay.ToString(); } public static Singleton GetInstance() { Console.WriteLine($"GetInstance {DateTime.Now.TimeOfDay}"); Thread.Sleep(500); return Nested.instance; } private class Nested { static Nested() { } internal static readonly Singleton instance = new Singleton(); } }

Теперь при выполнении той же программы мы получим полноценную Lazy-реализацию:

Main 16:37:18.4108064 hello GetInstance 16:37:18.4208062 Singleton ctor 16:37:18.4218065 16:37:18.4228061

Реализация через класс Lazy

Еще один способ создания синглтона представляет использование класса Lazy:

Public class Singleton { private static readonly Lazy lazy = new Lazy(() => new Singleton()); public string Name { get; private set; } private Singleton() { Name = System.Guid.NewGuid().ToString(); } public static Singleton GetInstance() { return lazy.Value; } }

Введение

Singleton – один из самых простых шаблонов для понимания. Основное назначение – гарантировать существование только одно экземпляра класса. Причиной обычно является следующее: требуется только один объект исходного класса и Вам необходимо, что бы объект был доступен в любом месте приложения, т.е. глобальный доступ.

В качестве примера может служить класс для хранения установочных параметров(Settings). Settings class – хороший пример шаблона Singleton, потому что его данные не измены (единственный путь изменения установочных параметров это редактирование файла установочных параметров) и часто используются в различных частях приложения. Кроме того, создание новых объектов класса Settings, где это необходимо, требует ресурсы, что расточительно, т.к. объекты будут идентичны.

Определение

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

Пример для PHP5

Пример для PHP5(без реализации конкретных методов класса Settings)
class Settings {
private $settings = array();
private static $_instance = null;
private function __construct() {
// приватный конструктор ограничивает реализацию getInstance ()
}
protected function __clone() {
// ограничивает клонирование объекта
}
static public function getInstance() {
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
public function import() {
// ...
}
public function get() {
// ...
}
}

Реализация шаблона Singleton

Ключoм реализации шаблона Singleton является статическая переменная, переменная чье значение остается неизменным при исполнении за ее приделами. Это позволяет сохранить объект оригинальным между вызовами статического метода Settings::getInstance(), и возвратить ссылку на него при каждом последующем вызове метода.
Имейте так же в виду, что конструктор, как правило, приватный. Что бы обеспечить использование всегда только одного объекта Settings мы должны ограничить доступ к конструктору, что бы при попытке создания нового объекта возникала ошибка. Так же следует иметь в виду, что данные ограничения не возможны в PHP4. Программы