Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
конспект_укр.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.07 Mб
Скачать

Призначення патерну Singleton

Часто в системі можуть існувати сутності тільки в єдиному екземплярі, наприклад, система ведення системного журналу повідомлень або драйвер дисплея. У таких випадках необхідно вміти створювати єдиний екземпляр деякого типу, надавати до нього доступ ззовні і забороняти створення декількох екземплярів того ж типу.

Патерн Singleton надає такі можливості.

Опис патерну Singleton

Архітектура патерну Singleton заснована на ідеї використання глобальної змінної, що має такі важливі властивості:

Така змінна доступна завжди. Час життя глобальної змінної - від запуску програми до її завершення.

Надає глобальний доступ, тобто, така змінна може бути доступна з будь-якої частини програми.

Однак, використовувати глобальну змінну деякого типу безпосередньо неможливо, оскільки існує проблема забезпечення єдиності примірника, а саме, можливе створення декількох змінних того ж самого типу (наприклад, стекових).

Для вирішення цієї проблеми патерн Singleton покладає контроль над створенням єдиного об'єкта на сам клас. Доступ до цього об'єкта здійснюється через статичну функцію-член класу, яка повертає покажчик або посилання на нього. Цей об'єкт буде створений тільки при першому зверненні до методу, а всі наступні виклики просто повертають його адресу. Для забезпечення унікальності об'єкта, конструктори і оператор привласнення оголошуються закритими. UML-діаграма класів патерну Singleton

UML-діаграма класів патерну Singleton

Патерн Singleton часто називають вдосконаленою глобальної змінної.

Реалізація патерну Singleton

Класична реалізація Singleton

Розглянемо найбільш часто зустрічається реалізацію патерну Singleton.

// Singleton.h

class Singleton

{

  private:

    static Singleton * p_instance;

    // Конструкторы и оператор присваивания недоступны клиентам

    Singleton() {}

    Singleton( const Singleton& ); 

    Singleton& operator=( Singleton& );

  public:

    static Singleton * getInstance() {

        if(!p_instance)          

            p_instance = new Singleton();

        return p_instance;

    }

};

  

// Singleton.cpp

#include "Singleton.h"

  

Singleton* Singleton::p_instance = 0;

Клієнти запитують єдиний об'єкт класу через статичну функцію-член getInstance (), яка при першому запиті динамічно виділяє пам'ять під цей об'єкт і потім повертає покажчик на цю ділянку пам'яті. Впоследcтвіі клієнти повинні самі подбати про звільнення пам'яті за допомогою оператора delete.

Остання особливість є серйозним недоліком класичної реалізації шаблону Singleton. Так як клас сам контролює створення єдиного об'єкта, було б логічним покласти на нього відповідальність і за руйнування об'єкта. Цей недолік відсутній в реалізації Singleton, вперше запропонованої Скоттом Мейерс.

// Singleton.h

class Singleton

{

private:

    Singleton() {}

    Singleton( const Singleton&); 

    Singleton& operator=( Singleton& );

public:

    static Singleton& getInstance() {

        static Singleton  instance;

        return instance;

    }   

};

Усередині getInstance() використовується статичний екземпляр потрібного класу. Стандарт мови програмування C + + гарантує автоматичне знищення статичних об'єктів при завершенні програми. Дострокового знищення і не потрібно, тому що об'єкти Singleton зазвичай є довгоживучими об'єктами. Статична функція-член getInstance() повертає НЕ покажчик, а посилання на цей об'єкт, тим самим, ускладнюючи можливість помилкового звільнення пам'яті клієнтами.

Наведена реалізація патерну Singleton використовує так звану відкладену ініціалізацію (lazy initialization) об'єкта, коли об'єкт класу инициализируется ні при старті програми, а при першому виклику getInstance(). В даному випадку це забезпечується тим, що статична змінна instance оголошена всередині функції - члена класу getInstance(), а не як статичний член даних цього класу. Відкладену ініціалізацію, в першу чергу, має сенс використовувати в тих випадках, коли ініціалізація об'єкта являє собою дорогу операцію і не завжди використовується.

На жаль, у реалізації Мейерс є недоліки: складність створення об'єктів похідних класів і неможливість безпечного доступу декількох клієнтів до єдиного об'єкту в багатопотокової середовищі.

Покращена версія класичної реалізації Singleton

З урахуванням усього вищесказаного класична реалізація патерну Singleton може бути поліпшена.

// Singleton.h

class Singleton;  // опережающее объявление

  

class SingletonDestroyer

{

  private:

    Singleton* p_instance;

  public:   

    ~SingletonDestroyer();

    void initialize( Singleton* p );

};

  

class Singleton

{

  private:

    static Singleton* p_instance;

    static SingletonDestroyer destroyer;

  protected:

    Singleton() { }

    Singleton( const Singleton& );

    Singleton& operator=( Singleton& );

   ~Singleton() { }

    friend class SingletonDestroyer;

  public:

    static Singleton& getInstance();   

};

  

// Singleton.cpp

#include "Singleton.h"

  

Singleton * Singleton::p_instance = 0;

SingletonDestroyer Singleton::destroyer;

  

SingletonDestroyer::~SingletonDestroyer() {  

    delete p_instance;

}

void SingletonDestroyer::initialize( Singleton* p ) {

    p_instance = p;

}

Singleton& Singleton::getInstance() {

    if(!p_instance)     {

        p_instance = new Singleton();

        destroyer.initialize( p_instance);    

    }

    return *p_instance;

}

Ключовою особливістю цієї реалізації є наявність класу SingletonDestroyer, призначеного для автоматичного руйнування об'єкта Singleton. Клас Singleton має статичний член SingletonDestroyer, який ініціалізується при першому виклику Singleton :: getInstance () створюваним об'єктом Singleton. При завершенні програми цей об'єкт буде автоматично зруйнований деструктором SingletonDestroyer (для цього SingletonDestroyer оголошений іншому класу Singleton).

Для запобігання випадкового видалення користувачами об'єкту класу Singleton, деструктор тепер вже не є загальнодоступним як раніше. Він оголошений захищеним.

Використання декількох взаємозалежних одинаків

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

Як гарантувати, що до моменту використання одного одинаки, примірник іншого залежного вже створений?

Як забезпечити можливість безпечного використання одного одинаки іншим при завершенні програми? Іншими словами, як гарантувати, що в момент руйнування першого одинаки в його деструкторі ще можливе використання другого залежного одинаки (тобто другий одинак до цього моменту ще не зруйнував)?

Управляти порядком створення одинаків відносно просто. Наступний код демонструє один з можливих методів.

// Singleton.h

class Singleton1

{

  private:

    Singleton1() { }

    Singleton1( const Singleton1& ); 

    Singleton1& operator=( Singleton1& );

  public:

    static Singleton1& getInstance() {

        static Singleton1 instance;

        return instance;

    }   

};

  

class Singleton2

{

  private:    

    Singleton2( Singleton1& instance): s1( instance) { }

    Singleton2( const Singleton2& ); 

    Singleton2& operator=( Singleton2& );

    Singleton1& s1;

  public:

    static Singleton2& getInstance() {

        static Singleton2 instance( Singleton1::getInstance());

        return instance;

    }   

};

  

// main.cpp

#include "Singleton.h"

  

int main()

{

    Singleton2& s = Singleton2::getInstance(); 

    return 0;

}

Об'єкт Singleton1 гарантовано инициализируется раніше об'єкта Singleton2, так як в момент створення об'єкту Singleton2 відбувається виклик Singleton1::getInstance().

Набагато складніше керувати часом життя одинаків. Існує кілька способів це зробити, кожен з них володіє своїми достоїнствами і недоліками і заслуговують окремого розгляду. Обговорення цієї непростої теми залишається за рамками даної лекції. Незважаючи на гадану простоту патерну Singleton (використовується всього один клас), його реалізація не є тривіальною.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]