Стандартні потоки і потокові класи.

Література

Операції класу

У третій згори секції прямокутника записуються операції або методи класу. Операція (operation) є деяким сервісом, що надається кожним екземпляром класу на певну вимогу. Сукупність операцій характеризує функціональний аспект поведінки класу. Кожній операції класу відповідає окремий рядок:

<квантор видимості><ім 'я операції>(список параметрів): <вираження типу повертаного значення>{рядок- властивість}

Для квантора видимості і імені операції справедливо вусі те ж що і для квантора видимості атрибуту і імені атрибуту.

Список параметрів є переліком розділених комою формальних параметрів, кожен з яких може бути представлень в наступному виді:

<вид параметра><ім 'я параметра>:<вираження типу>=<значення параметра за умовчанням>.

Тут вид параметра - одне з ключових слів in, out або inout зі значенням in за умовчанням. Ім 'я параметра це ідентифікатор відповідного формального параметра. Вираження типу є покладом від конкретної мови програмування специфікацією типу повертаного значення. Вид значення за умовчанням залежить від конкретної мови програмування. Ті ж торкається і типу повертаного значення. Двокрапка і вираження типу повертаного значення можуть бути опущені, якщо операція не повертає ніякого значення.

Операція із зоною дії на обвішай клас показується підкресленням імені і рядка вираження типу. За умовчанням під областю операції розуміється об' єкт класу.

Операція, яка не може змінювати стан системи і, відповідно, не має ніякого побічного ефекту, позначається рядком- властивістю "{запитий}" ("{query}"). У осоружному випадку, операція може змінювати стан системи, хоча немає ніяких гарантій, що вона це робитиме.

67. Катаев М.Ю. Объектно-ориентированное программирование: Учебное пособие. - Томск: Томский межвузовский центр дистанционного образования, 2000. - 145 с.

68. Кент Рейсдорф и Кен Хендерсон BORLAND C++BUILDER. ОСВОЙ САМОСТОЯТЕЛЬНО

69. Лафоре Р. Обьектно–ориентированное программирование в С++. 4-е изд.: – СПб: Питер, 2004 – 902 с.

70. Львов М.С., Співаковський О.В. Вступ до об’єктно-орієнтоване програмування. Навчальний посібник. - Херсон: ХГПУ, 2000.- 238 с.:іл.

71. М.С. Львов. О.В. СпіваковськийВступ до об’єктно-орієнтоване програмування. Навчальний посібник. - Херсон: ХГПУ, 2000.- 238 с.:іл.

72. Шилдт Герберт. Искусство программирования на С++. Санкт-Питербург, 2005.-496 с., ил.

73. Шилдт, Герберт. C++: руководство для начинающих, 2-е издание. : Пер. с англ. — М. : Издатель- ский дом "Вильяме", 2005. — 672 с. : ил. — Парал. тит. англ.

74. Эккель Брюс. Философия С++. Введение в стандартный С++ (2-е издание). - СПб: Питер, 2004 – 573 с.

75. Эккель Брюс. Философия С++. Практическое программирование (2-е издание). - СПб: Питер, 2004 – 610 с., ил.

76. Юркова Т. А., Ушаков Д. М., Паскаль для школьников. — СПб.: Питер, 2010. — 256 с.: ил.

77. Visual Studio 2010 и введение в .NET 4.0 и для профессионалов. Макки, Алекс : Пер. с англ. — М. : ООО И. Д. Вильямс". 2010. — 416 с. : ил. — Парал, тит. англ.

 

Лекція № 8

Тема 8. Бібліотека стандартних шаблонів STL (Standard Template Library)

Основні концепції STL

План лекції

1.Бібліотека стандартних шаблонів - STL (призначення, основні можливості, сфера застосування).

2.Стандартні потоки і потокові класи.

3.Стрічки (основні операції та функції; переваги порівняно із C - стрічками).

4.Поняття про контейнери, ітератори, функціональні об' єкти та алгоритми STL.

Зміст лекції

##include <vector>

##include <list>

##include <deque>

using namespace std:

Контейнер вектор є аналогом звичайного масиву, за винятком того, що він автоматично виділяє і звільняє пам'ять в міру необхідності. Контейнер ефективно обробляє довільну вибірку елементів з по-мощью операції індексації [] або методу at. Проте вставка елементу в будь-яку позицію, окрім кінця вектору, неефективна. Для цього потрібно буде зрушити усі наступні елементи шляхом копіювання їх значень. З цієї ж причини неефективним є видалення будь-якого елементу, окрім останнього.

Контейнер список організовує зберігання об' єктів у вигляді двозвязкового списку. Кожен елемент списку містить три поля: значення елементу, покажчик на передуючий, і покажчик на наступний елементи списку. Вставка і видалення працюють ефективно для будь- якої позиції елементу в списку. Проте список не підтримує довільного доступу до своїх елементів: наприклад, для вибірки n -го елементу треба послідовно вибрати попередні n - 1 елементів.

Контейнер двостороння черга {дек) багато в чому аналогічний вектору, елементи зберігаються в безперервній області пам'яті. Але на відміну від вектору двостороння черга ефективно підтримує вставку і видалення першого елементу (так само, як і останнього).

Існує п'ять способів визначити об'єкт для послідовного контейнера.

Створити порожній контейнер:

vector<int> vecl: list<string> list1:

Створити контейнер заданого розміру і ініціалізувати його елементи зна-чениями за умовчанням:

vector<string> vec1(100) : list<double> list1(20) :

Створити контейнер заданого розміру і ініціалізувати його елементи вказаним значенням:

vector<string> vecl(100. ''Hello"!) : deque<int> decl(300. - 1) :

Створити контейнер і ініціалізувати його елементи значеннями діапазону [first, last) елементів іншого контейнера:

int arr[7] = {15. 2. 19. - 3. 28. 6. 8}: vector<int> v1(arr. arr + 7) : list<int> lst(v1.beg() + 2. v1.end()) :1

Створити контейнер і ініціалізувати його елементи значеннями елементів іншого однотипного контейнера :

vector<int> v1:

// // додати в v1 елементи vector<int> v2(v1) :

Для вставки і видалення останнього елементу контейнера будь-якого з трьох розглядаючих класів призначені методи push_back() і pop_back(). Крім того, список і черга (але не вектор) підтримують операції вставки і видалення першого елементу контейнера push_front() і pop_front(). Врахуйте, що методи pop_back() і pop_front() не повертають видалене значення. Щоб рахувати перший елемент, використовується метод front(), а для прочитування останнього елементу - метод bаск().

Шаблони функцій та класів.

В процесі роботи над програмами, що використовують контейнери, часто прихо-дится виводити на екран їх поточний вміст. Приведемо шаблон функції, що вирішує цю задачу для будь-якого типу контейнера, :

template <class Т> void print(T& cont){

typename T:: const_iterator p = cont.begin() : if (cont.empty())

cout " "Container is empty".: for (p: p != cont.end() : ++p) cout " *p " ' ': cout " endl:

}

Зверніть увагу на службове слово typename, з якого починається оголошення ітератора р. Річ у тому, що бібліотека STL "знає", що Т::iterator - це деякий тип, а компілятор C++ такого знання не має. Тому без typename нормальні компілятори фіксують помилку.

Тепер можна користуватися функцією print(), включаючи її визначення в початковий файл з програмою, як, наприклад, в наступному експерименті з чергою:

##include <iostream>

##include <deque> using namespace std:

/* ... /* .. визначення функції print .. */ int main() {

deque<int> dec: print(dec) // Container
dec.push_back(4) : print(dec) // 4
dec.push_front(3) : print(dec) // 3 4
dec.push_back(5) : print(dec) // 3 4 5
dec.push_front(2) : print(dec) // 2 3 4 5
dec.push_back(6) : print(dec) // 2 3 4 5 6
dec.push_front(1) : print(dec) // 1 2 3 4 5
return 0:

Адаптери контейнерів

Спеціалізовані послідовні контейнери - стек, черга і черга з пріоритетами - не є самостійними контейнерними класами, а реалізовані на основі розглянутих вище класів, тому вони називаються адаптерами контейнерів.

Стек

Шаблонний клас stack (заголовний файл <stack>) визначений як

template <class Т. class Container = deque<T> > class stack { /* .. */ }:

де параметр Container задає клас-прототип. За умовчанням для стека прототипом є клас deque. Сенс такої реалізації полягає в тому, що спеціалізований клас просто перевизначає інтерфейс класу-прототипу, обмежуючи його тільки тими методами, які потрібні новому класу. У таблиці. 6.5 показано, як сформований інтерфейс класу stack з методів класу-прототипу.

Таблиця 6.5. Інтерфейс класу stack

Методи класу stack Методи класу-прототипу
push() push_back()
pop () pop_back()
top() back()
empty() empty()
size() size()

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

концепція ітераторів в стеку не підтримується. Нагадуємо, що метод pop () не повертає видалене значення. Щоб рахувати значення на вершині стека, використовується метод top().

Приклад роботи із стеком - програма вводить з файлу числа і виводить їх на екран в зворотному порядку:

int main() {

ifstream in ("inpnum.txt") : stack<int> s: int x:

while (in " x) s.push(x) :

while (!s.empty()){

cout " s.top() " ' ': s.pop() :

}

return 0:

}

Оголошення stack<int> s створює стек на базі двосторонньої черги (за умовчанням). Якщо з якихось причин нас це не влаштовує і ми хочемо створити стек на базі списку, то оголошення виглядатиме таким чином:

stack<int. list<int> > s:

8.1. Бібліотека стандартних шаблонів - STL (призначення, основні можливості, сфера застосування).

Стандартна бібліотека шаблонів (STL).

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

Склад STL.

Ядро бібліотеки утворять три елементи: контейнери, алгоритми й ітератори.

Контейнери (containers) - це об' єкти, призначені для зберігання інших елементів. Наприклад, вектор, лінійний список, множина.

Асоціативні контейнери (associative containers) дозволяють за допомогою ключів отримати швидкий доступ до значень, що зберігаються в них.

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

Алгоритми (algorithms) виконують операції над умістом контейнера. Існують алгоритми для ініціалізації, сортування, пошуку, заміни вмісту контейнерів. Багато алгоритмів призначені для роботи з послідовністю (sequence), що являє собою лінійний список елементів усередині контейнера.

Ітератори (iterators) - це об' єкти, які стосовно контейнера відіграють роль покажчиків. Сморід дозволяють одержати доступ до вмісту контейнера приблизно так само, як покажчики використовуються для доступу до елементів масиву.

З ітераторами можна працювати так само, як з вказівниками. До них можна застосувати операції *, інкремента, декремента. Типом ітератора оголошується тип iterator, що визначений у різних контейнерах.

Існує п'ять типів ітераторів:

1. Ітератори введення (input_iterator) підтримують операції рівності, разыменования й інкремента.

==i, ++i, i++, *i++

Спеціальним випадком ітератора введення є istream_iterator.

2. Ітератори виведення (output_iterator) підтримують операції разыменования, припустимі тільки з лівої сторони присвоювання, і інкремента.

++++i, i++, *i=t, *i++=t

Спеціальним випадком ітератора виводу є ostream_iterator.

3. Односпрямовані Ітератори (forward_iterator) підтримують всі операції ітераторів уведення/виводу й, крім того, дозволяють без обмеження застосовувати присвоювання.

==i, ++i, i++, *i++

4. Двонаправлені Ітератори (biderectional_iterator) мають всі властивості forward - ітераторів, а також мають додаткову операцію декремента (--i, i--, *i--), що дозволяє їм проходити контейнер в обох напрямках.

5. Ітератори довільного доступу (random_access_iterator) мають всі властивості biderectional - ітераторів, а також підтримують операції порівняння й адресної арифметики, тобто безпосередній доступ по індексі.

i+=n, i+n, i-=n, i - n, i1 - i2, i[n], i1<i2, i1<=i2, i1>i2, i1>=i2

 

Потоки не є характеристикою С++, а були введені ще в ANSI C. У С++ мезанізм потоків оновлено і змінено. Проте загальний механізм під'єднання потоків залишився.

Потік (введенню/виводу) - є абстрактним поняттям, яку відноситься до будь-якого перенесення даних між прибудовами видавання та приймання інформації.

Читання даних з потоку називається операцією витягування даних з потоку (extracting, getting orfetching data). Реалізовується оператором ви-тягуваня з потоку operator>>*. Розміщення даних в потік називаєтться операцією вставки даних в потік (inserting, putting or storing data ). Реалізовується оператором вставки в потік operator<<*.

Потік визначається як послідовність байтів і не залежить від конкретного прибудую, з яким відбувається обмін інформацією. Пере-да-вання даних здійснюється через спеціально виділену пам'ять, яка нази-ва- ється буфером. Фактичне передавання даних відбувається при виовді після заповнення буфера, а при вводі - якщо буфер є порожнім. За напрямком обміну інформацією потоки можні розділити на вхідні (дані вводяться в пам'ять), вихідні (дані вибераютьяс з пам' яті) і двонаправленні.

Потоки пов' язані з фізичним пристроєм за допомогою низько- рівневого інтерфейсу введенню/виводу С++. Оскільки усі потоки введенню/виводу (у/в) діють однаково, то система в/в представляє собою єдиний зручний ін -терфейс, незважаючи на ті, що програмісту доводитися працювати з аб-со-лютно різними за природою об' єк -тами. Іншою перевагою потоків є контроль типів, та здатність до розширення, тобто здатність працювати з типами, оголошеними користувачем.

Основним недоліком потоків є зменшення швидкодії програми, яку в залежності від реалізації компілятора може бути значним.

За типом пристроів, з якими працюють потоки, їх умовно можна по- ділити на потоки стандартні, файлові і рядковіі. Стандартні потоки ви-користовуються для виведення інформації на еркан та зчитування її з кла- віатури. Файлові потоки забезпечують файловий ввід/вивід. Рядкові потоки використовуються для роботи з масивами символьних рядків в оперативні пам' яті.

Під година вико-нання програми (за умови підмикання основного пото-ко-вого інтерфейсного файлу iostream.h) автоматичний від -кри-ва-ються чотири по-то-ки С++, які вико-ристовуються для зв'язку з прис--троєм по замовчуванню. У таблиці.1 наведено список стан-дартних потоків. У випадку необхідності потоки можуть бути перена- правлені на інші пристрої.

Структура потокової системи введенню/виводу

Потоки С++ повністю реалізовані через класи (ієрархії класів). З метою використання потокових операцій в програмі треба під'єднати хоча б один інтерфейсний файл заголовків ФЗ. Основним серед них є iostream.h. У цьому файлі визначені набори ієрархій класів, які забезпечують ввід та вивід. З іншого боку, підмикання ФЗ fstream.h чи sstream.h* автоматичний підмикає і файл iostream.h, оскільки він для них є базовим.

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

Система потоків С++ базується на двох зв'язаних, проте різних, ієрархіях шаблонів. Деручи з них є ієрархією від базового класу-шаблону base_streambuf. Данії клас (реально екземпляр цього класу streambuf) забезпечує операції в/в нижнього рівня. Верхній рівень забез-пе- чується ієрархією від класу-шаб-лону base_ios. У свою чергу клас-шаблон base_ios є базовим для класів base_istream, base_os - tre - am і base_iostream. Влас-не останні класи-шаблони викорис-товуються для створення кла- сів, які у свою чергу забез-пе-чу-ють ство-рен-ня потоків в/в**. В таблиці.2 наве-дено відповідності класів - шаблонів і породжуваних ними потокових класів.

Потокові класи утворюють дві зв' язані ієрахії класів. На рис.1- 2 наведені ієрархії потокової системи в/у в реалізації Borlаnd C++.

Список потокових класів визначених стан-дартом ISO/ІЕС 1882 наведень у таблиці.3.

У кінцевому результаті користувач отримує дві основні взаємозв'язані ієрархії, що забезпечують систему потокового в/в С++.

 

 

8.3. Стрічки (основні операції та функції; переваги порівняно із C - стрічками).

Шаблони

Для того, аби зрозуміти усю потужність методології ТОП звернемо увагу на кращі досягнення багаторічних досліджень у напрямку стандартизації та типізації рішень для створення ефективного програмного забезпечення. Тут відзначимо два ключових напрямки: стандартна бібліотека шаблонів (STL - Standard Template Library) та шаблони проектування (Design Patterns).

Стандартна бібліотека шаблонів (STL) увібрала у собі багаторічний досвід програмістів, що постійно стикалися з одними й тими ж "тактичними" завданнями: як зберігати і маніпулювати даними, які алгоритми краще застосувати у тій чи іншій ситуації, і т.д., і т.п. Оскільки програмісти не ті люди, які люблять працювати, а тим більше виконувати рутинну роботові більше одного разу, смороду, наприклад, не хочуть щоразу створювати власні функції для зберігання даних (незалежно від їх природи) у стечу, якщо можна використовувати універсальний стік. Так само непотрібно щоразу придумувати яким методом краще сортувати якийсь там вектор (масив), якщо добрі люди вже один раз придумали як це краще робити і оформили свої ідеї у стандартний алгоритм. Загалом механізм використання STL повністю грунтується на механізмі створення та використання шаблонів класів, тому трохи далі розглянемо лише принципи використання шаблонів класів, а STL і її можливості залишимо на самостійне вивчення (крім того бібліотека дуже об' ємна і різноманітна, щоб з неї можна було вибрати лише якісь окремі аспекти для стислого висвітлення).

Шаблони проектування, навідміну від STL, є узагальненим досвідом застосування типових прийомів розробки і успішного створення програмного забезпечення. Шаблони проектування - це не шаблони, як конструкція програмування, а стереотипний підхід до розв'язання типових завдань, що виникають перед програмістом. Власне ідея шаблонів проектування як таких запозичена з архітектури. Взагалі різних шаблонів створення для програмного

забезпечення є доволі багато, але власне під терміном "Шаблони проектування" (design patterns), переважно, мають на увазі 23 шаблони, розроблені групою відомих програмістів (теоретиків та основоположників сучасного ТОП), відомою як GoF - Gang of Four ("банда чотирьох") - Ерік Гамма (Erich Gamma), Річард Хелм (Richard Helm), Ральф Джонсон (Ralph Johnson) та Джон Вліссідс (John Vlissides). Навідміну від "тактичних" досягнень, втілених у STL, досягнення втілені у шаблонах проектування можна назвати "стратегічними". Зважаючи на великі обсяги теоретичного і прикладного матеріалу, необхідні для знайомства з шаблонами проектування, далі зупинимося лише на базовій класифікації і призначенні цих шаблонів.

Шаблони класів

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

template <опис параметрів шаблону>

class НазваКласу

{

/* /* опис класу */

};

Використаємо наведень нами раніше приклад, зокрема, клас "Товар".

Припустимо, що мі не знаємо точно, у яких числах вестиметься облік цін (наприклад, дійсні чи цілі). Поклад від валюти у якій планується встановлювати ціни, чи від власне цін на товари, які продаватимуться, нам можуть знадобитися різні можливості. Тому можемо описати шаблон класу (зробимо це у файлі tovar.h) :

##ifndef _tovar_h_

##define _tovar_h_

##include<string>

##include<iostream>22

using namespace std;

template <class Currency> class Tovar

{

static Currency sum;

Currency price;

string units;

public:

static Currency GetSum(void);

bool Sell(void);

Tovar();

Tovar(Currency);

Tovar(Currency, const string&);

friend ostream& operator<< <Currency>(ostream&, Tovar<Currency>&);

};

template <class Currency> Currency Tovar<Currency>::sum = 0;

template <class Currency> Currency Tovar<Currency>::GetSum(void)

{

return sum;

}

template <class Currency> bool Tovar<Currency>::Sell(void)

{

if(price>0)

{

sum+=price;

price = 0;

return true;

}

else return false;

}

template <class Currency> Tovar<Currency>::Tovar()

{

price = 1;

units = "piece";

}

template <class Currency> Tovar<Currency>::Tovar(Currency p)

{

price = (p>0)?p :0;

units = "piece";

}

template <class Currency> Tovar<Currency>::Tovar(Currency p, const string& ov)

{

price = (p>0)?p :0;

units = ov;

}

template <class Currency> ostream& operator<< (ostream& os, Tovar<Currency>& t)

{

if(t.price>0) return os<<"$"<<((Currency)t.price)<<" per 1 "<<t.units;

else return os<<"Sold 1 "<<t.units<<" Cash=="<< Tovar<Currency>::sum;

}

##endif23

Одразу відзначимо кілька важливих особивостей у описі шаблону класу :

1. Ініціалізація статичних полів та опис будь-яких методів шаблону класу повинні бути здійснені у одному файлі з декларацією шаблону. Якщо цього не зробити, а застосувати звичайне відокремлення опису від декларації, то, нажаль, компілятор MS Visual Studio видає купу помилок.

2. Використання значень за замовчуванням у конструкторах не бажане, щоб уникнути помилок компілятора, пов'язаних із неоднозначністю описів.3. Можна декларувати дружні функції і оператори, в тому числі перевантажені, але у MS Visual Studio 2008 та 2010 це слід робити так: friend ostream& operator<< <Currency>(ostream&, Tovar<Currency>&);

або так:

friend ostream& operator<< <>(ostream&, Tovar<Currency>&);

тобто, явно або неявно вказуючи на присутність параметрів шаблону.

4. Описи методів та дружніх функцій обов'язково повинні містити вказівку на приналежність до шаблону - на качану опису потрібно вказувати шаблон з параметрами. Наприклад, опис дружного перевантаженого оператора зсуву повине мати такий вигляд :

template <class Currency>

ostream& operator << (ostream& os, Tovar<Currency>& t)

{ /* тіло оператора (так само може бути функції чи методу) */ }

Аналогічно описуються методи, задекларовані у класі.

5. Статичні поля будуть визначені, насправді не для шаблону вцілому, а для класів з однаковими параметрами шаблону. У цьому легко переконатись

на прикладі (два останніх рядки у головній програмі).

Отже, для перевірки роботи описаного шаблону використаємо таку головну програму (файл maintovar.cpp) :

##include<iostream>

##include" tovar.h"

using namespace std;

void main(void)

{

Tovar<int> tovar1_int;

Tovar<int> tovar_int(10);

Tovar<float> tovar_float(25.95","kg);

cout <<"INT typed Tovar :"<<endl<<tovar1_int<<endl<<tovar_int<<endl;

cout << "FLOAT typed Tovar :" << endl << tovar_float << endl;

cout << "NOW SALE!!"! <<endl;

if(tovar1_int.Sell()) cout << "Sell ok! ";

cout << "tovar 1_int : " << tovar1_int << endl;

if(tovar_int.Sell()) cout << "Sell ok! ";

cout << "tovar_int: " << tovar_int << endl;

if(tovar_float.Sell()) cout << "Sell ok! ";

cout << "tovar_float: " << tovar_float << endl;

cout << "Cash<INT>: " << Tovar<int>::GetSum() << endl;24

cout << "Cash<FLOAT>: " << Tovar<float>::GetSum() << endl;

}

Результатом роботи цієї програми буде такий вивід на екран:

INT typed Tovar :

$$1 per 1 piece

$$10 per 1 piece

FLOAT typed Tovar :

$$25.95 per 1 kg

NOW SALE!!!

Sell ok! tovar1_int: Sold 1 piece Cash==1

Sell ok! tovar_int: Sold 1 piece Cash==11

Sell ok! tovar_float: Sold 1 kg Cash==25.95

Cash<INT>: 11

Cash<FLOAT>: 25.95

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

Шаблони проектування

Шаблони проектування (Design Patterns) загалом поділяють на три групи:

 Породжуючі або твірні шаблони (Creational Patterns).

 Структурні шаблони (Structural Patterns).

 Шаблони поведінки (Behavioral Patterns).

До першої групи (Creational Patterns) належать такі шаблони :

Абстрактна фабрика (Abstract Factory) - забезпечує інтерфейс для

створення сімейств пов'язаних чи залежниз об' єктів без вказання їх

конкретних класів.

Будівельник (Builder) - відокремлює побудову доладного об' єкту від його представлення, що дозволяє одному й тому ж процесу побудови створювати різні представлення.

Фабричний метод (Factory method) - визначає інтерфейс для створення об' єкту, але дозволяє підкласам вирішувати, які саме екземпляри класів створювати. Фабричний метод дозволяє класу перекласти інстанціювання (створення екземплярів) на підкласи.

Прототип (Prototype) - вказує відіа об' єктів для створення з допомогою прототипу (прототипного екземпляру) і створює нові об' єкти копіюванням цього прототипу.

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

До другої групи (Structural Patterns) належать такі шаблони :

Адаптер (Adapter) - перетворює інтерфейс класу до іншого інтерфейсу, якого очікують клієнти. Адаптер дозволяє працювати разом класам, які через несумісність інтерфейсів інакше не змогли б. Інша назва цього шаблону -"Обгортка" (Wrapper).

Міст (Bridge) - відокремлює абстракцію від її реалізації, що дозволяє обом (абстракції і реалізації) змінюватися незалежно. Використовується у ситуаціях, коли потрібно створити клас, який буде змінюватися сам по собі і будуть змінюватися дії, які він повинний виконувати.

Компонувальник (Composite) - поєднує об' єкти у деревовидні структури для пердставлення ієрархії типу "частина-ціле". Дозволяє клієнтам звертатися до окремих об' єктів і їх композицій однаково.

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

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

Для порівняння "Адаптер" забезпечує точно визначений інтерфейс із підтримкою поліморфної поведінки. Водночас, навідміну від шаблону "Замісник", який працює непомітно (є прозорим) для клієнта, "Фасад" акцентовано перебирає на собі усі повноваження по взаємодії із прихованими за ним підсистемами.

Легковаговик (Flyweight) - використовує розподілене використання ресурсу (пам' яті) для ефективної підтримки великої кількості дрібних об' єктів.

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

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

Замісник (Proxy) - забезпечує сурогатний або замінюючий об' єкт для іншого об'кту з метою конролю доступу до останнього. У найбільш загальному розумінні цей шаблон лише надає інтерфейс до чогось іншого. Фактично це клас з інтерфейсом ідентичним до інтерфейсу сутності, якові він повинний заміщати або контролювати. Таким чином, використання об' єкта - замісника для клієнта є ідентичним до використання об' єкту - оригіналу, але дозволяє спростити, покращити чи захистити доступ до цього оригіналу. Застосування

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

До третьої групи (Behavioral Patterns) належать такі шаблони :

Ланцюг відповідальності (Chain of responsibility) - уникає поєднання відправника запиту із його одержувачем шляхом надання більш ніж одному об' єкту можливості обробити запитий. З об' єктів, що можуть обробити запитий формується ланцюг, вздовж якого запитий передається до тихий пір, поки не буде успішно оброблений. Насправді, якщо серед об' єктів - обробників запиту передбачити об' єкти, які будуть самостійно вирішувати куди далі передавати обробку одержаного запиту, то ланцюг може бути перетворений у дерево чи будь-яку іншу структуру, згідно з якою відбуватиметься розподіл та делегування повноважень на обробку запитів.

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

Перекладач (Interpreter) - враховуючи мову, визначає представлення для її граматики та одночасно перекладача, що використовує це представлення для перекладу речень мови. Це специфічний шаблон проектування, пов'язаний із необхідністю розбору спеціалізованих комп'ютерних мов: мови для роботи з базами даних (наприклад, SQL), спеціалізовані мови для опису комунікаційних

протоколів, мови опису регулярних виразів (використовуються для розбору і пошуку фрагментів у інших виразах), мови інтерпретації обчислюваних виразів

((наприклад, обернений польський запис) худе. Шаблон вказує як саме здійснювати розбір речень мови. Основна ідея шаблону полягає у наявності окремого класу для шкірного символу (термінального чи нетермінального) спеціалізованої комп' ютерної мови. Для створення синтаксичного дерева розбору цьому шаблону допомагає шаблон "Компонувальник" (Composite), який допомагає у розборі (перекладі) речення.

Ітератор (Iterator) - забезпечує спосіб доступу до елементів складеного об' єкту послідовно, але не розкриваючи внутрішнього влаштування цього об' єкту. Складений об' єкт тут виступає у ролі контейнера для елементів, які він містить, причому суть внутрішньої організації (впорядкування) елементів у контейнері приховується від клієнта. Водночас, щоб забезпечити послідовний доступ до (всіх по черзі) елементів контейнеру, потрібен ітератор. Цей шаблон відокремлює алгоритми доступу від контейнерів, хоча у деяких випадках алгоритми доступу є специфічними для контейнера і їх неможливо відокремити. Інколи шаблон "Ітератор" ще називають "Курсор" (Cursor).

Посередник (Mediator) - визначає об' єкт, що інкапсулює спосби взаємодії між об' єктами. Посередник сприяє слабкому зв'язуванню об' єктів, утримуючи їх від явного посилання один на одного, і дозволяє варіювати їх взаємодії самостійно (незалежно). Фактично цей шаблон надає уніфікований інтерфейс для набору інтерфейсів у деякій підсистемі. Застосування шаблону корисне у випадках, коли у програмі створюється значна кількість різних класів і взаємодія між об'ктами може бути досить доладною. Водночас, необхідність з часом вносити зміни і доповнювати програму новими класами суттєво ускладнює взаємодії між різнми об' єктами, що може унеможливити подальшу розробку і вдосконалення програми. Посередник дозволяє, уникнувши прямих зв' язків між об' єктами, зменшити ваємну залежність і вирішити проблему ефективної взаємодії між об' єктами.

Хранитель (Memento) - без порушення інкапсуляції захоплює і надає втілення внутрішньому стану об' єкту, що дозволяє згодом відновити цей стан.

Фактично ця можливість відновлювати попередній стан об' єкту є чимось на зразок скасування дій шляхом відкату. Методологія роботи шаблону грунтується на тому, що у взаємодії деякого об' єкту і суб' єкту, що може здійснювати зміни цього об' єкту, передбачається створення об' єктом свого хранителя (memento) - спеціалізованого об' єкту, який зберігає інформацію про поточний стан свого породжуючого об' єкту. Перед качаном модифікації суб' єкт дає запитий об' єкту про створення хранителя, якого об' єкт повертає

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

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

Спостерігач (Observer) - визначає залежність між об' єктами типу "один до багатьох", де зміна стану одного об' єкту (спостережуваного - observable) автоматичний викликає повідомлення і здійснення відповідних змін (оновлення) в усіх решта залежних об' єктів (спостерігачів - observers). Суть взаємодії між спостережуваним об' єктом і його спостерігачами у тому, що спостерігачі реєструються у спостережуваного об' єкту (як спостерігачі), а спостережуваний об' єкт зобов'язаний повідомляти спостерігачів про будь- які свої зміни стану.

Далі спостерігача самостійно повинні вирішувати, що робити із одержаною інформацією чи як реагувати на отримане повідомлення. Інша назва цього шаблону "Видавець/передплатник" (Publish/subscribe), можливо, більш яскраво розкриває його суть: передплатники реєструються у видавця і очікують на видання, яку видавець розсилає усім передплатникам.

Стан (State) - дозволяє об' єкту змінювати свою поведінку, коли змінюється його внутрішній стан. Виглядатиме це як зміна класу об' єкта. Цей шаблон також ще називають "Об' єкти для станів" (Objects for states).

Принципове призначення шаблону - представляти стан об' єкту. І цей шаблон надає допустимий шлях для об' єкту частково змінити свій тип під година роботи програми (at runtime).

Стратегія (Strategy) - визначає сімейство алгоритмів, інкапсулює кожен із них та дозволяє зробити їх взаємозамінними. Шаблон передбачає можливість змінювати алгоритм незалежно від клієнтів, що ним користуються. Фактично це особливий шаблон, який дозволяє обирати алгоритм під година роботи програми (at runtime) поклад від ситуації яка складається. Наприклад, від типу покладу даних, які потрібно обробити, алгоритми обробки можуть радикально відрізнятися. Але про ті, які конкретно дані прийдуть на обробку, програма дізнається вже лише у процесі роботи. Тому потрібен механізм для вибору адекватного алгоритму і шаблон "Стратегія" визначає цей механізм.

Шаблонний метод (Template method) - визначає скелет алгоритму у операції, відкладаючи деякі крокі для визначення у підкласах. Цей шаблон дозволяє підкласам перевизначити окремі крокі алгоритму, не змінюючи загальну структуру цього алгоритму. Принцип реалізації шаблону доволі простий і грунтується на перевантаженні (overriding) методів у класах-нащадках. Фактично, деякий базовий клас визначає "шаблонний метод" у термінах окремих операцій (методів класу, що можуть бути навіть чисто віртуальними). Нащадки цього базового класу, перевизначивши відповідні операції, вже як власні методи, додають власну специфіку і забезпечують виконання вусі того ж глобально визначеного алгоритму.

Відвідувач (Visitor) - представляє операцію, яка повинна бути виконана на елементах структури об' ктів. Шаблон дозволяє визначити нову операцію без зміни класів елементів, на яких вона працює. Цее шлях для відокремлення алгоритму від структури об' єктів, на якій цей алгоритм працює. Практичний результат такого відокремлення полягає у здатності додавати нові операції до існуючої структури об' ктів без внесення змін у цю структру. Це один із шляхів дотримуватися відкрито -закритого принципу (open/closed principle - OCP). По суті, шаблон "Відвідувач" дозволяє додавати нові віртуальні функції у сімейство класів, не змінюючи самих класів. Замість цього створюється клас- відвідувач, який реалізує усі відповідні спеціалізації віртуальної функції. Клас- відвідувач приймає на вхід посилання на екземпляр і реалізує цілі через механізм "Подвійної відправки" (механізм, що передбачає відправку виклику функції до різних конкретних функцій поклад від типів парі об' єктів, задіяних у виклику під година виконання програми).

Серед корисних шаблонів, які часто доводитися зустрічати, але які не належать до визначених GoF шаблонів проектування (design patterns), відмітимо шаблон "Відкладена ініціалізація" (Lazy Initialisation) - тактика відтермінування створення об' єкту, обчислення значення, чи будь-якого іншого витратного (доладного, ресурсоємного, тривалого, худо) процесу до моменту годині, коли це справді буде необхідним. Цей шаблон належить до групи породжуючи шаблонів. (Цей шаблон не належить до Design Patternsвизначених GoF.)

Загальні вхідні дані для курсу лабораторних зайняти Курс лабораторних робіт розрахований на 64 аудиторних години та складається з 8 лабораторних робіт. Завдання передбачають виконання усіх лабораторних робіт на мові C++ у середовищі Microsoft Visual Studio.