Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Проектування інформаційних систем.doc
Скачиваний:
95
Добавлен:
21.09.2019
Размер:
28.77 Mб
Скачать

15.2. Відношення між об'єктами

15.2.1. Типи відношень

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

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

  • зв'язкок;

  • агрегація.

Ці два типи відношень називаються відношеннями старшинства й "батько/нащадок" відповідно.

15.2.2. Зв'язки

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

На рис. 15.2 показано кілька різних зв'язків. Вони відзначені лініями й позначають шляхи проходження повідомлень. Самі повідомлення показані стрілками (відповідно їхньому напрямку) і позначені іменем операції. На рисунку об'єкт aController зв'язаний із двома об'єктами класу DisplayItem (об'єкти a і b). У свою чергу, обидвоє, імовірно, пов'язані з aView, але нам цікавитиме тільки один із цих зв'язків. Тільки вздовж зв'язку один об'єкт може послати повідомлення іншому.

Зв'язок між об'єктами й передача повідомлень звичайно одностороння (як на рисунку; хоча технічно вона може бути й взаємною). Подібний поділ прав і обов'язків типовий для добре структурованих об'єктних систем. Зауважте також, що хоча передане повідомлення ініціалізоване клієнтом (у цьому випадку aController), дані передаються в обох напрямках. Наприклад, коли aController викликає операцію move для пересилання даних об'єктові а, дані передаються від клієнта серверу, однак під час виконання операції isUnder над об'єктом b, результат передається від сервера клієнтові.

Беручи участь у зв'язку, об'єкт може виконувати одну з таких трьох ролей:

Актор

(Actor - це діяч, виконавець. А виконавець ролей, це і є актор). Об'єкт може впливати на інші об'єкти, але сам ніколи не піддається впливу інших об'єктів; у певному змісті це відповідає поняттю активний об'єкт.

Сервер

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

Агент

Такий об'єкт може виступати як в активній, так і в пасивній ролі; як правило, об'єкт-агент створюється для виконання операцій в інтересах якого-небудь об'єкта-актора або агента.

Рис. 15.2. Зв'язки.

На рис. 15.2 об'єкт aController виступає як актор, об'єкт a - як агент і об'єкт aView - як сервер.

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

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

// Число хвилин, що минули

typedef unsigned int Minute;

Тепер опишемо сам клас TemperatureRamp, що за змістом задає функцію часу від температури:

class TemperatureRamp {

public:

TemperatureRamp();

virtual ~TemperatureRamp();

virtual void clear();

virtual void bind (Temperature, Minute);

Temperature TemperatureAt (Minute);

protected:

...

};

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

Насправді в змісті поведінки нам треба щось більше, ніж просто залежність температури від часу. Нехай, наприклад, відомо, що на 60-й хвилині повинна бути досягнута температура 80С, а на 180-й – 50С. Запитується, якою вона повинна бути на 120-й хвилині? Це вимагає лінійної інтерполяції, так що необхідна від абстракції поведінка ускладнюється.

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

class TemperatureController {

public:

TemperatureController(Location);

~TemperatureController();

void process(const TemperatureRamp&);

Minute schedule(const TemperatureRamp&) const;

private:

...

};

Тип Location був визначений у розділі 14. Зауважте, що ми не очікуємо успадкування від цього класу й тому не повідомляємо в ньому ніяких віртуальних функцій.

Операція process забезпечує основну поведінку цієї абстракції; її призначення - передати графік зміни температури нагрівачу, встановленому в конкретному місці. Наприклад, оголосимо:

TemperatureRamp growingRamp;

TemperatureController rampController(7);

Тепер задамо пару точок і завантажимо план у контролер:

growingRamp.bind (250, 60);

growingRamp.bind(150, 180);

rampController.process(growingRamp);

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

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

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

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

  • Сервер глобальний стосовно клієнта.

  • Сервер (або показник на нього) переданий клієнтові як параметр операції.

  • Сервер є частиною клієнта.

  • Сервер локально породжується клієнтом під час виконання якої-небудь операції.

Який саме із цих способів вибрати - залежить від тактики проектування.

Синхронізація. Коли один об'єкт посилає через зв'язок повідомлення іншому, пов'язаному з ним об’єкту, то кажуть, що вони синхронізуються. У багатопотоковій системі об'єкти вимагають витонченішої схеми передачі повідомлень, щоб розв'язати проблеми взаємного виключення, які типові для паралельних систем. Активні об'єкти самі по собі виконуються як потоки, тому присутність інших активних об'єктів на них звичайно не впливає. Якщо ж активний об'єкт має зв'язок з пасивним, можливі наступні три підходи до синхронізації:

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

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

  • Синхронний - семантика пасивного об'єкта забезпечується в присутності багатьох потоків керування; взаємне виключення забезпечує сервер.

Всі об'єкти, описані в цьому розділі, були послідовними.