
- •1. Основні терміни технології програмування
- •2. Класифікація програмного забезпечення
- •Поняття життєвого циклу розробки програмного забезпечення.
- •Етапи розробки
- •Базові моделі розробки програмних продуктів.
- •Вимоги до методології та технології розробки пп
- •Каскадна модель
- •3. Модель прототипування програмного продукту
- •4. Спіральна модель
- •5. Модель rad
- •6. Модель екстремального програмування (xp)
- •7. Модель msf (Microsoft Solutions Framework)
- •Принципи й види налагодження.
- •Аксіоми налагодження.
- •Автономне налагодження модуля.
- •Комплексне налагодження програмного засобу.
- •Інструменти розробки програмних засобів.
- •Інструментальні середовища розробки й супроводу програмних засобів.
- •Інструментальні середовища програмування.
- •Поняття комп'ютерної технології розробки програмних засобів і її робочі місця.
- •Інструментальні системи технології програмування.
- •Приклад реалізації класу Log.
- •Розробка програмного продукту з двома потоками
- •Визначення крапок контролю програмного продукту.
- •Визначення кількості викликів
- •Визначення ступеня покриття
- •Фундаментальні проблеми профілювання.
- •Причини рефакторингу
- •Підстави для проведення рефакторингу
- •Прийоми рефакторингу
- •Автоматизований рефакторинг
- •1. Принципи повторного використання елементів програм
- •2. Створення шаблонів форм у вигляді файлів
- •3. Використання шаблонів форм у новому проекті
- •4. Збереження шаблонів форм в депозитарії
- •5. Використання шаблонів форм із депозитарію
- •Шаблони класів на мові програмування с#.
- •Приклади шаблонів (класів шаблонів).
- •1. Використання підпрограм в оброблювачах подій
- •2. Звертання до активного компонента не за ім’ям
- •2. Обробка групи компонентів
- •4. Обробка компонентів як масиву
- •5. Сортування даних у компонентах
- •1. Принципи модульного програмування
- •2. Принцип «приховання даних»
- •3. Поняття модуля в Object Pascal
- •4. Структура модульного файлу
- •5. Створення модуля в Object Pascal
- •6. Створення модуля з переліком стандартних діалогів
- •7. Використання текстових констант у модулях
- •8. Створення підпрограм для обробки компонентів
- •1. Поняття dll
- •2. Створення dll бібліотеки в Delphi
- •3. Внесення форм в dll
- •4. Використання dll бібліотеки
- •Питання для самоконтролю
- •Використання регулярних виразів у програмах.
- •1. Призначення зовнішніх компонентів
- •2. Установка й видалення зовнішніх компонентів
- •3. Установка й видалення бібліотек компонентів
- •4. Запуск зовнішніх програм і файлів
- •Питання для самоконтролю
- •1. Загальні принципи технології com
- •2. Робота з com-сервером Microsoft Word
- •Робота з документами в Microsoft Word
- •Використання шаблону для формування документів
- •Робота з таблицями
- •Вставка малюнків і їх форматування
- •1. Операції з Com-Сервером Microsoft Excel
- •Робота із книгами в Microsoft Excel
- •Робота з аркушами книги в Microsoft Excel
- •Використання шаблону для формування книги
- •Формування таблиці
- •6. Форматування чарунок
- •Види довідкових систем
- •Інші засоби підтримки користувача
- •2.Формати довідників
- •3.Створення довідки у форматі html Help
- •4.Створення контекстної довідки
- •5.Інтеграція довідкового файлу в додаток
- •Перелік шаблонів, що породжують
- •Перелік структурних шаблонів
- •Перелік шаблонів поведінки
- •Призначення патерну Singleton
- •Реалізація патерну Singleton
- •Результати застосування патерну Singleton
- •Призначення патерна Observer
- •Постановка проблеми, що вирішується за допомогою патерна
- •Структура патерна Observer
- •Приклад патерна Observer
- •Реалізація патерна Observer
- •Реалізація патерну Observer: до та після
- •Призначення патерну Strategy
- •Опис патерну Strategy
- •Реалізація патерну Strategy
- •Призначення патерна Factory Method
- •Опис патерну Factory Method
- •Реалізація патерна Factory Method
Призначення патерну 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 (використовується всього один клас), його реалізація не є тривіальною.