PrIS
.pdf
311
відношення використання між їх класами TemperatureController і TemperatureRamp.
class TemperatureController { public: TemperatureController(Location); ~TemperatureController();
void process(const TemperatureRamp&);
Minute schedule(const TemperatureRamp&) const; private:
Heater h; };
Клас TemperatureRamp тут використовується як частина сигнатури функції-члена process; це дає нам підстави сказати, що клас TemperatureController користується послугами класу
TemperatureRamp.
Клієнти і сервери. Відношення використання між класами відповідає рівноправному зв'язку між їхніми екземплярами. Це те, у що перетворюється асоціація, якщо виявляється, що одна з її сторін (клієнт) користується послугами іншої (сервера). Приклад клієнт-серверних відношень показаний на рис. 15.8.
Рис. 15.8. Відношення використання.
Насправді, один клас може використовувати інший по-різному. У нашому прикладі це відбувається в сигнатурі інтерфейсної функції. Можна задати, що TemperatureController всередині реалізації функції schedule використовує, наприклад, екземпляр класу Predictor. Відношення цілого й частини тут немає, оскільки цей об'єкт не входить в об'єкт TemperatureController, а тільки використовується. У типовому випадку таке відношення використання проявляє себе, якщо в реалізації якої-небудь операції відбувається оголошення локального об'єкту використовуваного класу.
Строге відношення використання іноді трохи обмежене, оскільки клієнт має доступ тільки до відкритої частини інтерфейсу сервера. Іноді з тактичних міркувань ми повинні порушити інкапсуляцію, для чого, властиво, і служать "дружні" відношення класів у C++.
312
15.4.6. Інсталювання (Параметризація)
Приклади. Наша перша спроба сконструювати клас Queue (черга) була не дуже успішною, оскільки нам не вдалося зробити його безпечним щодо типів. Ми можемо значно вдосконалити нашу абстракцію, якщо вдамося до конструкції параметризованих класів, що підтримується мовами C++ і Eiffel.
Template<class Item> class Queue { public:
Queue();
Queue(const Queue<Item>&); virtual ~Queue();
virtual Queue<Item>& operator=(const Queue<Item>&);
virtual int operator==(const Queue<Item>&) const;
int operator!=(const Queue<Item>&) const; virtual void clear();
virtual void append(const Item&); virtual void pop();
virtual void remove(int at); virtual int length() const; virtual int isEmpty() const; virtual const Item& front() const; virtual int location(const void*); protected:
...
};
У цьому новому варіанті не використовується ідіома void*, замість цього об'єкти вміщуються в чергу і беруться з неї через клас item, оголошений як аргумент шаблону.
Параметризованний клас не може мати екземплярів, поки він не буде інстальований. Оголосимо дві черги – чергу цілих чисел і чергу екранних об'єктів:
Queue<int> intQueue; Queue<DisplayItem*> itemQueue;
Об'єкти intQueue та itemQueue – це екземпляри зовсім різних класів, які навіть не мають загального суперкласу. Проте, вони отримані з одного параметризованого класу Queue. У другому випадку ми помістили в чергу показники. Завдяки цьому, будь-які об'єкти підкласів DisplayItem, поміщені в чергу, не будуть "зрізатися", але збережуть свою поліморфну поведінку.
313
Інсталяція безпечна з погляду типів. За правилами C++ буде відкинута будь-яка спроба помістити в чергу або добути з її що-небудь, крім, відповідно, цілих чисел і різновидів DisplayItem.
Відношення між параметризованим класом Queue, його інсталяціями для класу DisplayItem і екземпляром itemQueue показані на рис. 15.9.
Рис. 15.9. Інсталювання.
Узагальнені класи. Існує чотири основних способи створювати такі класи, як параметризованний клас Queue. По-перше, ми можемо використати макровизначення. Саме так це було в ранньому C++, але цей підхід прийнятний тільки для невеликих проектів, тому що макроси перебувають поза семантикою мови, і що більше, при кожному інсталюванні створюється нова копія програмного коду. По-друге, можна використати пізнє зв'язування й успадкування, як це робиться у Smalltalk. При такому підході ми можемо будувати тільки неоднорідні контейнерні класи, тому що в мові немає засобу ввести потрібний клас елементів контейнера; кожний елемент у контейнері трактується як екземпляр деякого вилученого базового класу. Третій спосіб реалізований у мовах сімейства Object Pascal, які мають і сильні типи, й успадкування, але не підтримують ніякого різновиду параметризованих класів. У цьому випадку доводиться створювати узагальнені контейнери, як у Smalltalk, але використовувати явну перевірку типу об'єкту, перед тим як вміщувати його в контейнер. Нарешті, є власне параметризовані класи, що вперше з'явилися в CLU. Параметризований клас – це шаблон для побудови інших класів; шаблон може бути параметризованим іншими класами, об'єктами або операціями. Параметризований клас має бути інстальованим перед створенням екземплярів. Механізм узагальнених класів є в C++ і Eiffel.
Як видно з рис. 15.9, щоб інсталювати параметризований клас Queue ми повинні використати інший клас, наприклад, DisplayItem.
314
Завдяки цьому відношення інсталяції майже завжди має на увазі відношення використання.
Мейер зазначає, що успадкування - потужніший механізм, ніж узагальнені класи, і що через успадкування можна одержати більшість переваг узагальнених класів, але не навпаки. Нам здається, що краще,
коли мови підтримують і те, й інше.
Параметризовані класи корисні далеко не тільки для створення контейнерів. Наприклад, Страуструп відзначає їхнє значення для узагальненої арифметики.
При проектуванні узагальнені класи дозволяють виразити деякі властивості протоколів класів. Клас експортує операції, які можна виконувати над його екземплярами. Навпаки, параметризований арґумент класу служить для імпорту класів і значень, що надають деякий протокол. C++ перевіряє їх взаємну відповідність при компіляції, коли фактично й відбувається інсталяція. Наприклад, ми могли б визначити впорядковану чергу об'єктів, відсортованих за деяким критерієм. Цей параметризований клас повинен мати арґумент (клас Item), і вимагати від цього арґументу певної поведінки (наявність операції обчислення порядку). При інсталяції як клас Item може бути будь-який клас, що має відповідний протокол. Отже, поведінка класів у сімействі, що походять від одного параметризованого класу, може змінюватися в досить широких межах.
15.4.6. Метакласи
Як було сказано, будь-який об'єкт є екземпляром якого-небудь класу. Що буде, якщо ми спробуємо із самими класами поводитися як з об'єктами? Для цього нам треба відповісти на запитання, що ж таке клас класу? Відповідь – це метаклас. Іншими словами, метаклас – це клас, екземплярами якого є класи.
Класи надають програмістові інтерфейс для визначення об'єктів. Якщо так, то бажано, щоб і самі класи були об'єктами, так щоб ними можна було маніпулювати, як всіма іншими описами.
У мовах типу Smalltalk первинне призначення метакласу – підтримка змінних класу (які є загальними для всіх екземплярів цього класу), операції ініціалізації змінних класу і створення одиничного екземпляра метакласу. Метаклас Smalltalk містить приклади використання його класів. Наприклад, як показано на рис. 15.10, ми могли б задати змінну класу nextId для метакласу TelemetryData, щоб виробляти ідентифікаційні мітки при створенні кожного екземпляру TelemetryData. Аналогічно, ми могли б визначити оператор утворення нових екземплярів класу, який би їх створював у деякій наперед виділеній пам'яті.
315
Рис. 15.10. Метакласи.
Хоча у C++ метакласів немає, семантика його конструкторів і деструкторів служить цілям, аналогічним до тих, що викликали існування метакласів. C++ має засоби підтримки і змінних класу, і операцій метакласу. У C++ можна описати члени даних або функції класу як статичні (static), що буде означати: цей елемент є загальним для всіх екземплярів класу. Статичні члени класу у C++ еквівалентні змінним класу у Smalltalk. Статична функція-член класу відіграє роль операцій метакласу у Smalltalk.
Як ми вже зазначали, у CLOS апарат метакласів ще потужніший, ніж у Smalltalk. Через нього можна змінювати саму семантику елементів: слідування класів, узагальнені функції та методи. Головна перевага - можливість експериментувати з іншими об’єктно-орієнтованими парадигмами і створювати такі інструменти для розробника, як браузери класів і об'єктів.
У CLOS є визначений клас із іменем standard-class, який є метакласом для всіх нетипізованих класів, визначених за допомогою defclass. У цьому метакласі є метод make-instance, який створює екземпляри. Крім того, у ньому визначена вся техніка роботи зі списком слідування класів. Все це можна змінити.
Методи й узагальнені функції у CLOS теж можна розглядати як об'єкти, тому що вони трохи відрізняються від звичайних об'єктів. У сукупності об'єкти, що відповідають класам, методам і узагальненим функціям, називаються метаоб’єктами. Кожний метод є екземпляром визначеного класу standard-method, а кожна функція є екземпляром визначеного класу standard-generic-function. Оскільки поведінку цих визначених класів можна змінити, вдається впливати на трактування методів і узагальнених функцій.
316
15.5. Взаємозв'язок класів і об'єктів
15.5.1. Відношення між класами й об'єктами
Класи й об'єкти – це окремі, але тісно зв'язані поняття. Зокрема, кожний об'єкт є екземпляром якого-небудь класу; клас може породжувати будь-яку кількість об'єктів. У більшості практичних випадків класи статичні, тобто всі їх особливості та зміст визначені у процесі компіляції програми. Із цього випливає, що будь-який створений об'єкт відноситься до строго фіксованого класу. Самі об'єкти, навпаки, у процесі виконання програми створюються і знищуються.
Як приклад розглянемо класи й об'єкти для задачі керування повітряним рухом. Найважливіші абстракції в цій сфері – літаки, графіки польотів, маршрут і коридори в повітряному просторі. Трактування цих класів об'єктів за самим їх визначенням досить статичне. Інакше неможливо було б побудувати ніякої інформаційної системи, яка використовує такі загальнозрозумілі факти, як такі, що літаки можуть злітати, літати й приземлятися, а також що ніякі два літаки не можуть перебувати одночасно в одній і тій самій точці.
Об'єкти цих класів, навпаки, динамічні. Набір маршрутів польотів змінюється не дуже часто. Набагато швидше змінюється множина літаків, яка перебуває в польоті. Частота, з якою літаки займають і залишають повітряні коридори, ще більша.
15.5.2. Роль класів і об'єктів в аналізі та проектуванні
Під час етапу аналізу й ранніх стадій проектування розв’язуються дві основні задачі:
виявлення класів і об'єктів, що становлять словник предметної області;
побудова структур, які забезпечують взаємодію об'єктів, за допомогою яких виконуються вимоги задачі.
Упершому випадку говорять про ключові абстракції задачі (сукупність класів і об'єктів), у другому – про механізми їх реалізації (сукупність структур).
На ранніх стадіях увага проектувальника зосереджується на зовнішніх проявах ключових абстракцій і механізмів. Такий підхід створює логічний каркас системи: структури класів і об'єктів. На наступних фазах проекту, включаючи реалізацію, увага переключається на внутрішню поведінку ключових абстракцій і механізмів, а також їх фізичне подання. Прийняті у процесі проектування рішення задають архітектуру системи: архітектуру процесів і архітектуру модулів.
317
Висновки
1.Об'єкт характеризується станом, поведінкою та ідентичністю.
2.Структура і поведінка однакових об'єктів описується в загальному для них класі.
3.Стан об'єкту визначає його статичні й динамічні властивості.
4.Поведінка об'єкту характеризується зміною його стану у процесі взаємодії (за допомогою передавання повідомлень) з іншими об'єктами.
5.Ідентичність об'єкту – це його відмінність від всіх інших об'єктів.
6.Ієрархія об'єктів може будуватися на принципах зв'язку або аґреґації.
7.Множина об'єктів з однаковою структурою і поведінкою є класом.
8.Шість типів ієрархій класів включають: асоціацію, успадкування, аґреґацію, використання, інсталювання і метаклас.
9.Класи й об'єкти, що утворюють словник предметної області, називаються ключовими абстракціями.
10.Структура, що поєднує множину об'єктів і забезпечує їх спільне цілеспрямоване функціонування, називається механізмом.
Контрольні запитання
1.Природа класів і об'єктів.
2.Стан об'єкту.
3.Поведінка об'єкту.
4.Ролі й відповідальності об'єкту.
5.Ідентичність об'єктів.
6.Відношення між об'єктами.
7.Аґреґація об'єктів.
8.Що таке клас?
9.Інтерфейс і реалізація.
10.Відношення між класами.
11.Асоціація.
12.Успадкування.
13.Множинне успадкування.
14.Поліморфізм.
15.Аґреґація.
16.Використання.
318
17.Інсталювання.
18.Мета класи.
19.Відношення між класами й об'єктами.
319
РОЗДІЛ 16. Класифікація
Важливість правильної класифікації
Ідентифікація класів і об'єктів
Ключові абстракції й механізми
Класифікація — засіб впорядкування знань. В об’єктноорієнтовному аналізі визначення загальних властивостей об'єктів допомагає знайти загальні ключові абстракції й механізми, що, своєю чергою приводить нас до простішої архітектури системи менеджменту знань. На жаль, наразі не розроблені строгі методи класифікації й немає правил, що дозволяють виділяти класи й об'єкти. Немає таких понять, як "ідеальна структура класів", "правильний вибір об'єктів". Як і в багатьох технічних дисциплінах, вибір класів є компромісним рішенням.
16.1. Важливість правильної класифікації
На одній з конференцій програмістам було задане запитання: "Якими правилами ви керуєтеся при визначенні класів і об'єктів?" Страуструп, розробник мови C++, відповів: "Це як пошук святого Грааля. Не існує панацеї". Габріель, один з розробників CLOS, сказав: "Це запитання, на яке немає простої відповіді. Я просто пробую". На щастя, є багатий досвід класифікації в інших науках, на основі якого розроблені методики об’єктно-орієнтовного аналізу. Кожна така методика пропонує свої правила (евристики) ідентифікації класів і об'єктів. Вони й будуть предметом цього розділу.
16.1.1. Класифікація й об’єктно-орієнтовне проектування
Визначення класів і об'єктів — одне з найскладніших завдань об’єктно-орієнтовного проектування. Ця робота зазвичай містить у собі елементи відкриття й винаходу. За допомогою відкриттів ми розпізнаємо ключові поняття й механізми, які утворять словник предметної області. За допомогою винаходу ми конструюємо узагальнені поняття, а також нові механізми, які визначають правила взаємодії об'єктів. Тому відкриття й винахід — невід'ємні частини успішної класифікації. Метою класифікації є знаходження загальних властивостей об'єктів. Класифікуючи, ми поєднуємо в одну групу об'єкти, що мають однакову будову або однакову поведінку.
320
Розумна класифікація, безсумнівно, — частина будь-якої науки. Невід'ємним завданням науки є побудова змістовної класифікації об'єктів або ситуацій, що спостерігаються. Така класифікація істотно полегшує розуміння основної проблеми й подальший розвиток наукової теорії. Та сама філософія відноситься й до інженерної справи. В області будівельної архітектури й міського планування для архітектора професійною діяльністю є розроблення проектів, яка керується образами, які він тримає у своїй свідомості в цей момент, і його здатністю комбінувати ці образи при створенні нового проекту.
Не дивно, що класифікація торкається багатьох аспектів об’єктноорієнтовного проектування. Вона допомагає визначити ієрархії узагальнення, спеціалізації й аґреґації. Знайшовши загальні форми взаємодії об'єктів, ми вводимо механізми, які стануть фундаментом реалізації нашого проекту. Класифікація допомагає правильно визначити модульну структуру. Ми можемо розташувати об'єкти в одному або різних модулях, залежно від ступеня подібності об'єктів; зчеплення й зв’язність — лише міри цієї подібності.
Класифікація відіграє значну роль при розподілі процесів між процесорами. Ми скеровуємо процеси на один процесор або на різні залежно від того, як ці процеси зв'язані один з одним.
16.1.2. Труднощі класифікації
Приклади класифікації. Об'єктом є щось таке, що має чіткі межі. Насправді це не зовсім так. Межі предметів часто невизначені. Наприклад, подивіться на ногу. Спробуйте визначити, де починається й закінчується коліно. У розмовній мові важко зрозуміти, чому саме ці звуки визначають слово, а не є частиною якогось довшого слова. Уявіть собі, що ви проектуєте текстовий редактор. Що вважати класом — літери чи слова? Як розуміти окремі фрази, речення, параграфи, документи? Як звертатися з довільними, не обов'язково осмисленими, блоками тексту? Що робити з реченнями, абзацами й цілими документами — чи відповідають такі класи нашій задачі?
Розумна класифікація — складна проблема. Оскільки є паралелі з аналогічними труднощами в об’єктно-орієнтовному проектуванні, розглянемо приклади класифікації у двох інших наукових дисциплінах: біології та хімії.
Аж до XVIII століття ідея про можливість класифікації живих організмів за ступенем складності була переважальною. Міра складності була суб'єктивною, тому не дивно, що людина виявилася у списку на першому місці. У середині XVIII століття шведський ботанік Карл Лінней запропонував досконалішу таксономію для класифікації організмів: він
