- •Міністерство освіти та науки України в.В. Литвин, н.Б. Шаховська Проектування інформаційних систем
- •Передмова наукового редактора серії підручників «комп’ютинґ»
- •1.1. Складність програмного забезпечення
- •1.2. Структура складних систем
- •1.2.1. Приклади складних систем
- •1.2.2. П'ять ознак складної системи
- •1.2.3. Організована і неорганізована складність
- •1.3. Методи подолання складності
- •1.3.1. Роль декомпозиції
- •1.3.3. Роль абстракції
- •1.3.4. Роль ієрархії
- •1.4. Про проектування складних систем
- •1.4.1. Інженерна справа як наука і мистецтво
- •1.4.2. Сенс проектування
- •4. Методи подолання складності.
- •2.1. Базові означення
- •2.2. Методи проектування інформаційних систем
- •2.3. Види інформаційних систем
- •2.4. Рівні моделей даних
- •3. Види інформаційних систем.
- •3.1. Методологія процедурно-орієнтованого програмування
- •3.2. Методологія об'єктно-орієнтованого програмування
- •3.3. Методологія об'єктно-орієнтованого аналізу і проектування
- •3.4. Методологія системного аналізу і системного моделювання
- •4.1. Передісторія. Математичні основи
- •4.1.1. Теорія множин
- •4.1.2. Теорія графів
- •4.1.3. Семантичні мережі
- •4.2. Діаграми структурного системного аналізу
- •4.3. Основні етапи розвитку uml
- •3. Семантичні мережі.
- •5.1. Принципи структурного підходу до проектування
- •5.2. Структурний аналіз
- •5.3. Структурне проектування
- •5.4. Методологія структурного аналізу
- •5.5. Інструментальні засоби структурного аналізу та проектування
- •6.1. Основні елементи
- •6.2. Типи зв’язків
- •6.3. Техніка побудови
- •6.4. Діаграма бізнес – функцій
- •6.4.1. Призначення діаграми бізнес-функцій
- •6.4.2. Основні елементи
- •7.1. Призначення діаграм потоків даних та основні елементи
- •7.1.1. Зовнішні сутності
- •7.1.2. Процеси
- •7.1.3. Накопичувачі даних
- •7.1.4. Потоки даних
- •7.2. Методологія побудови dfd.
- •8.1. Діаграма «сутність-зв’язок»
- •8.2. Діаграма атрибутів
- •8.3. Діаграма категоризації
- •8.4. Обмеження діаграм сутність-зв’язок
- •8.5. Методологія idef1
- •9.1. Основні елементи
- •9.2. Типи керуючих потоків
- •9.3. Принципи побудови
- •10.1. Структурні карти Константайна
- •10.2. Структурні карти Джексона
- •11.1. Призначення case-технологій
- •11.2. Інструментальний засіб bPwin
- •11.2.4. Інші діаграми bpWin
- •11.2.5. Моделі as is і to be
- •11.3.1. Основні властивості
- •11.3.2. Стандарт idef1x
- •11.4. Програмний засіб Visio
- •12.1. Системний аналіз області наукових досліджень
- •12.1.1. Аналіз предметної області
- •12.2. Системний аналіз біржі праці
- •12.2.1. Дерево цілей
- •12.2.2. Опис об’єктів предметної області
- •12.2.3. Концептуальна модель
- •14.1. Еволюція об'єктної моделі
- •14.1.1. Основні положення об'єктної моделі
- •14.2. Складові частини об'єктного підходу
- •14.2.1. Парадигми програмування
- •14.2.2. Абстрагування
- •14.2.3. Інкапсуляція
- •14.2.4. Модульність
- •14.2.5. Ієрархія
- •14.2.7. Паралелізм
- •14.2.8. Збереженість
- •14.3. Застосування об'єктної моделі
- •14.3.1. Переваги об'єктної моделі
- •14.3.2. Використання об'єктного підходу
- •14.3.3. Відкриті питання
- •15.1. Природа об'єкта
- •15.1.1. Що є й що не є об'єктом?
- •15.1.2. Стан
- •15.1.3. Поведінка
- •15.1.4. Ідентичність
- •Void drag(DisplayItem I); // Небезпечно
- •15.2. Відношення між об'єктами
- •15.2.1. Типи відношень
- •15.2.2. Зв'язки
- •15.2.3. Агрегація
- •15.3. Природа класів
- •15.3.1. Що таке клас?
- •15.3.2. Інтерфейс і реалізація
- •15.3.3. Життєвий цикл класу
- •15.4. Відношення між класами
- •15.4.1. Типи відношень
- •15.4.2. Асоціація
- •15.4.3. Успадкування
- •15.4.4. Агрегація
- •15.4.5. Використання
- •15.4.6. Інсталювання (Параметризація)
- •15.4.6. Метакласи
- •15.5. Взаємозв'язок класів і об'єктів
- •15.5.1. Відношення між класами й об'єктами
- •15.5.2. Роль класів і об'єктів в аналізі й проектуванні
- •16.1. Важливість правильної класифікації
- •16.1.1. Класифікація й об’єктно-орієнтовне проектування
- •16.1.2. Труднощі класифікації
- •16.2. Ідентифікація класів і об'єктів
- •16.2.1. Класичний і сучасний підходи
- •16.2.2. Об’єктно-орієнтований аналіз
- •16.3. Ключові абстракції й механізми
- •16.3.1. Ключові абстракції
- •16.3.2. Ідентифікація механізмів
- •17.1. Призначення мови uml
- •17.2. Загальна структура мови uml
- •17.3. Пакети в мові uml
- •17.4. Основні пакети мета-моделі мови uml
- •17.5. Специфіка опису мета-моделі мови uml
- •17.6. Особливості зображення діаграм мови uml
- •18.1. Варіант використання
- •18.2. Актори
- •18.3. Інтерфейси
- •18.4. Примітки
- •18.5. Відношення на діаграмі варіантів використання
- •18.5.1. Відношення асоціації
- •13.5.2. Відношення розширення
- •18.5.3. Відношення узагальнення
- •18.5.4. Відношення включення
- •18.6. Приклад побудови діаграми варіантів використання
- •18.7. Рекомендації з розроблення діаграм варіантів використання
- •19.1. Клас
- •19.1.1. Ім'я класу
- •19.1.2. Атрибути класу
- •19.1.3. Операція
- •19.2. Відношення між класами
- •19.2.1. Відношення залежності
- •19.2.2. Відношення асоціації
- •19.2.3. Відношення агрегації
- •19.2.4. Відношення композиції
- •19.2.5. Відношення узагальнення
- •19.3. Інтерфейси
- •19.5. Шаблони або параметризовані класи
- •19.6. Рекомендації з побудови діаграми класів
- •20.1. Автомати
- •20.2. Стан
- •20.2.1. Ім'я стану
- •20.2.2. Список внутрішніх дій
- •20.2.3. Початковий стан
- •20.2.4. Кінцевий стан
- •20.3. Перехід
- •20.3.2. Сторожова умова
- •20.3.3.Вираз дії
- •15.4. Складений стан і підстан
- •20.4.1. Послідовні підстани
- •20.4.2. Паралельні підстани
- •15.5. Історичний стан
- •20.6. Складні переходи
- •15.6.1. Переходи між паралельними станами
- •20.6.2. Переходи між складеними станами
- •20.6.3. Синхронізуючі стани
- •20.7. Рекомендації з побудови діаграм станів
- •21.1. Стан дії
- •21.2. Переходи
- •21.5. Рекомендації до побудови діаграм діяльності
- •22.1.1. Лінія життя об'єкта
- •22.1.2. Фокус керування
- •22.2. Повідомлення
- •22.2.1. Розгалуження потоку керування
- •22.2.2. Стереотипи повідомлень
- •22.2.3. Тимчасові обмеження на діаграмах послідовності
- •22.2.4. Коментарі або примітки
- •22.3. Приклад побудови діаграми послідовності
- •22.4. Рекомендації з побудови діаграм послідовності
- •23.1. Кооперація
- •23.2.1. Мультиоб'єкт
- •23.2.2. Активний об'єкт
- •23.2.3. Складений об'єкт
- •23.3. Зв'язки
- •23.3.1. Стереотипи зв'язків
- •23.4. Повідомлення
- •23.4.1. Формат запису повідомлень
- •23.5. Приклад побудови діаграми кооперації
- •23.6. Рекомендації з побудови діаграм кооперації
- •24.1. Компоненти
- •24.1.1. Ім'я компоненту
- •24.1.2. Види компонент
- •24.2. Інтерфейси
- •24.3. Залежності
- •24.4. Рекомендації з побудови діаграми компонент
- •25.1. Вузол
- •25.2. З'єднання
- •25.3. Рекомендації з побудови діаграми розгортання
- •26.1. Загальна характеристика case-засобу Rational Rose
- •26.2. Особливості робочого інтерфейсу Rational Rose
- •26.1.1. Головне меню програми
- •26.1.2. Стандартна панель інструментів
- •26.1.3. Вікно браузера
- •26.1.4. Спеціальна панель інструментів
- •26.1.5. Вікно діаграми
- •26.1.6. Вікно документації
- •26.1.7. Вікно журналу
- •26.3. Початок роботи над проектом у середовищі Rational Rose
- •26.4. Розроблення діаграми варіантів використання в середовищі Rational Rose
- •26.5. Розроблення діаграми класів у середовищі Rational Rose
- •26.6. Розроблення діаграми станів у середовищі Rational Rose
- •26.7. Розроблення діаграми послідовності в середовищі Rational Rose
- •26.8. Розроблення діаграми кооперації в середовищі Rational Rose
- •26.9. Розроблення діаграми компонентів у середовищі Rational Rose
- •26.10. Розроблення діаграми розгортання в середовищі Rational Rose
14.2. Складові частини об'єктного підходу
14.2.1. Парадигми програмування
Програмісти використовують в роботі одну мову програмування і слідують одному стилю. Вони програмують в парадигмі, нав'язаною мовою, яка ними використовується. Часто вони не розглядають альтернативні підходи до мети, а отже, їм важко побачити переваги стилю, що більше відповідає для вирішення завдання. Стиль програмування - це спосіб побудови програм, заснований на певних принципах програмування, і виборі відповідної мови, яка робить зрозумілими програми, написані в цьому стилі. Розрізняють п'ять основних різновидів стилів програмування, які перераховані нижче разом з властивими їм видами абстракцій:
процедурно-орієнтований алгоритми
об'єктно-орієнтований класи і об'єкти
логіко-орієнтовані цілі, часто виражені в термінах числення предикатів
орієнтований на правила правила "якщо-то"
орієнтований на обмеження інваріантні співвідношення
Важко визнати який-небудь стиль програмування найкращим у всіх сферах практичного застосування. Наприклад, для проектування баз знань найкращим є стиль, що орієнтується на правила, а для обчислювальних задач - процедурно-орієнтований. З нашого досвіду об'єктно-орієнтований стиль є найкращим для широкого кола задач; дійсно, ця парадигма часто служить архітектурним фундаментом, на якому ми засновуємо інші парадигми.
Кожний стиль програмування має свою концептуальну базу. Кожний стиль вимагає свій підхід та спосіб сприйняття вирішуваного завдання. Для об'єктно-орієнтованого стилю концептуальна база - це об'єктна модель. Вона має чотири головні елементи:
абстрагування;
інкапсуляція;
модульність;
ієрархія.
Ці елементи є головними в тому сенсі, що без будь-якого з них модель не буде об'єктно-орієнтованою. Окрім головних, є ще три додаткові елементи:
типізація;
паралелізм;
збережуваність.
Називаючи їх додатковими, ми маємо на увазі, що вони корисні в об'єктній моделі, але не є обов'язковими.
Без такої концептуальної основи можна програмувати на мові типу Smalltalk, Object Pascal, C++, CLOS, Eiffel або Ada, але з-під зовнішньої краси виглядатиме стиль FORTRAN, Pascal або С. Виразна здатність об'єктно-орієнтованої мови буде або втрачена, або спотворена. Але ще важливішим буде те, що при цьому буде мало шансів впоратися із складністю вирішуваних завдань.
14.2.2. Абстрагування
Сенс абстрагування. Абстрагування є одним з основних методів, який використовується для вирішення складних завдань. Абстрагування полягає в знаходженні подібності між певними об'єктами, ситуаціями або процесами реального світу, і в ухваленні рішень на основі цієї подібності. Спрощений опис або виклад системи, при якому одні властивості і деталі виділяються, а інші опускаються. Хорошою є така абстракція, яка підкреслює деталі, істотні для розгляду і використання, і опускає ті, які на даний момент неістотні. Підсумовуючи це, отримаємо таке визначення абстракції:
Абстракція виділяє істотні характеристики деякого об'єкту, що відрізняють його від всіх інших видів об'єктів і, таким чином, чітко визначає його концептуальні межі з точки зору спостерігача.
Абстрагування концентрує увагу на зовнішніх особливостях об'єкту і дозволяє відокремити найістотніші особливості поведінки від неістотних. Таке розділення сенсу і реалізації називається бар'єром абстракції, який ґрунтується на принципі мінімізації зв'язків, коли інтерфейс об'єкту містить лише істотні аспекти поведінки і нічого більшого. Корисним є ще один додатковий принцип, який називається принципом найменшого здивування, згідно якому абстракція повинна охоплювати всю поведінку об'єкту, але не більше і не менше, і не приносити сюрпризів або побічних ефектів, що знаходяться поза її сферою застосовності.
Вибір правильного набору абстракцій для заданої предметної області є головним завданням об'єктно-орієнтованого проектування. Зважаючи на важливість цієї теми їй цілком присвячена глава 4.
Існує цілий спектр абстракцій, починаючи з об'єктів, які майже точно відповідають реаліям предметної області, і закінчуючи об'єктами, що не мають права на існування. Ось ці абстракції, починаючи від найкорисніших до найменш корисних:
Абстракція сутності. Об'єкт є корисною моделлю деякої сутності ПО.
Абстракція поведінки. Об'єкт складається з узагальненої множини операцій.
Абстракція віртуальної машини. Об'єкт групує операції, які або разом використовуються вищим рівнем керування, або самі використовують деякий набір операцій нижчого рівня.
Довільна абстракція. Об'єкт включає набір операцій, які не мають одна з одною нічого спільного.
Ми прагнемо будувати абстракції сутності, оскільки вони прямо відповідають сутностям предметної області.
Клієнтом називається будь-який об'єкт, що використовує ресурси іншого об'єкту (званого сервером). Ми характеризуватимемо поведінку об'єкту послугами, які він надає іншим об'єктам, і операціями, які він виконує над іншими об'єктами. Такий підхід концентрує увагу на зовнішніх проявах об'єкту і приводить до ідеї, яку називають контрактною моделлю програмування: зовнішній прояв об'єкту розглядається з точки зору його контракту з іншими об'єктами, відповідно до цього повинно бути виконано і його внутрішній устрій (часто у взаємодії з іншими об'єктами). Контракт фіксує всі зобов'язання, які об'єкт-сервер має перед об'єктом-клієнтом. Іншими словами, цей контракт визначає відповідальність об'єкту - ту поведінку, за яку він відповідає.
Кожна операція, передбачена цим контрактом, однозначно визначається її формальними параметрами і типом значення, що повертається. Повний набір операцій, які клієнт може здійснювати над іншим об'єктом, разом з правильним порядком, в якому ці операції викликаються, називається протоколом. Протокол відображає всі можливі способи, якими об'єкт може діяти або піддаватися дії, повністю визначаючи тим самим зовнішню поведінку абстракції із статичної та динамічної точок зору.
Центральною ідеєю абстракції є поняття інваріанта. Інваріант - це деяка логічна умова, значення якої (істина або хибність) повинно зберігатися. Для кожної операції об'єкта можна задати передумови (інваріанти передбачувані операцією) і післяумови (інваріанти, яким задовольняє операція). Зміна інваріанта порушує контракт, пов'язаний з абстракцією. Зокрема, якщо порушена передумова, то клієнт не дотримує свої зобов'язання і сервер не може виконати своє завдання правильно. Якщо ж порушена післяумова, то свої зобов'язання порушив сервер, і клієнт не може більше йому довіряти. В разі порушення якої-небудь умови спрацьовує виняткова ситуація. Деякі мови мають засоби для роботи з винятковими ситуаціями: об'єкти можуть викликати виключення, щоб заборонити подальше опрацювання і попередити про проблему інші об'єкти, які у свою чергу можуть перейняти на себе перехоплення виключення і впоратися з проблемою.
Відмітимо, що поняття операція, метод і функція-член походять від різних традицій програмування (Ada, Smalltalk і C++ відповідно). Фактично вони позначають одне і те ж і надалі будуть взаємозамінні.
Всі абстракції володіють як статичними, так і динамічними властивостями. Наприклад, файл як об'єкт займає певний об'єм пам'яті на конкретному пристрої, має назву і містить інформацію. Ці атрибути є статичними властивостями. Конкретні ж значення кожної з перерахованих властивостей є динамічними і змінюються в процесі використання об'єкту: файл можна збільшити або зменшити, змінити його назву і вміст. У процедурному стилі програмування дії, що змінюють динамічні характеристики об'єктів, складають суть програми. Будь-які події пов'язані з викликом підпрограм і з виконанням операторів. Стиль програмування, орієнтований на правила, характеризується тим, що під впливом певних умов активізуються певні правила, які у свою чергу викликають інші правила, і так далі. Об'єктно-орієнтований стиль програмування пов'язаний з дією на об'єкти (в термінах Smalltalk - передачею об'єктам повідомлень). Так, операція над об'єктом породжує деяку реакцію цього об'єкту. Операції, які можна виконати над об'єктом, та реакція об'єкту на зовнішні дії визначають поведінку цього об'єкту.
Приклади абстракцій. Для ілюстрації сказаного вище наведемо декілька прикладів. У даному випадку ми сконцентруємо увагу не стільки на виділенні абстракцій для конкретної задачі (це детально розглянемо в главі 4), скільки на способі вираження абстракцій.
У тепличному господарстві, що використовує гідропоніку, рослини вирощуються на живильному розчині без піску, гравію або іншого ґрунту. Управління режимом роботи парникової установки - дуже відповідальна справа, залежна як від виду вирощуваних культур, так і від стадії вирощування. Потрібно контролювати цілий ряд чинників: температуру, вологість, освітлення, кислотність (показник рH) і концентрацію живильних речовин. У великих господарствах для вирішення цього завдання часто використовують автоматичні системи, які контролюють і регулюють вказані чинники. Просто кажучи, мета автоматизації полягає тут в тому, щоб при мінімальному втручанні людини добитися дотримання режиму вирощування.
Одна з ключових абстракцій в такій задачі - датчик. Відомо декілька різновидів датчиків. Все, що впливає на урожай, повинно бути виміряне, так що ми повинні мати датчики температури води і повітря, вологості, рН, освітлення і концентрації живильних речовин. Із зовнішньої точки зору датчик температури - це об'єкт, який здатний вимірювати температуру там, де він розташований. Що таке температура? Це числовий параметр, що має обмежений діапазон значень і певну точність, означає число градусів за Фаренгейтом, Цельсієм або Кельвіном. Що таке місце розташування датчика? Це деяке місце, що ідентифікується в теплиці, температуру в якому нам необхідно знати; таких місць, ймовірно, небагато. Для датчика температури істотно не стільки саме місце розташування, скільки той факт, що даний датчик розташований саме в даному місці і це відрізняє його від інших датчиків. Тепер можна поставити питання про те, які обов'язки датчика температури? Ми вирішуємо, що датчик повинен знати температуру в своєму місцезнаходженні і повідомляти її за запитом. Які ж дії може виконувати по відношенню до датчика клієнт? Ми приймаємо рішення про те, що клієнт може калібрувати датчик і отримувати від нього значення поточної температури.
Для демонстрації проектних вирішень буде використана мова C++. Ось опис, який встановлює абстрактний датчик температури на C++.
// Температура по Фаренгейту
typedef float Temperature;
// Число, що однозначно визначає положення датчика
typedef unsigned int Location;
class TemperatureSensor {
public:
TemperatureSensor (Location);
~TemperatureSensor();
void calibrate(Temperature actualTemperature);
Temperature currentTemperature() const;
private:
...
};
Тут для двох операторів визначення типів Temperature і Location вводять зручні псевдоніми для простих типів, і це дозволяє нам виражати свої абстракції на мові ПО. На жаль, конструкція typedef не визначає нового типу даних і не забезпечує його захисту. Наприклад, наступний опис в C++: "typedef int Count;" просто вводить синонім для примітивного типу int. Temperature - це числовий тип даних у форматі з плаваючою крапкою для запису температур в шкалі Фаренгейта. Значення типу Location позначає місце ферми, де можуть розташовуватися температурні датчики.
Клас Temperaturesensor - це лише специфікація датчика; справжня його суть прихована в його закритій (private) частині. Клас Temperaturesensor це ще не об'єкт. Власне датчики - це його екземпляри, і їх потрібно створити, перш ніж з ними можна буде оперувати. Наприклад, можна написати так:
Temperature temperature;
Temperaturesensor greenhouse1sensor(1);
Temperaturesensor greenhouse2sensor(2);
temperature = greenhouse1sensor.currentTemperature();
Розглянимо інші варіанти, пов'язані з операцією currentTemperature. Передумова включає припущення, що датчик встановлений в правильному місці в теплиці, а післяумова - що датчик повертає значення температури в градусах.
До цього часу ми вважали датчик пасивним: хтось повинен запитати у нього температуру, і тоді він відповість. Проте є і інший підхід. Датчик міг би активно стежити за температурою і повідомляти інші об'єкти, коли її відхилення від заданого значення перевищує заданий рівень. Абстракція від цього міняється мало: всього лише трохи, інакше формулюється відповідальність об'єкту. Які нові операції потрібні йому у зв'язку з цим? Звичайною ідіомою для таких випадків є зворотній виклик. Клієнт надає серверу функцію (функцію зворотнього виклику), а сервер викликає її, коли вважає за потрібне. Все це буде виглядати так:
class ActiveTemperatureSensor {
public:
ActiveTemperatureSensor (Location,
void (*f)(Location, Temperature));
~ActiveTemperatureSensor();
void calibrate(Temperature actualTemperature);
void establishSetpoint(Temperature setpoint,
Temperature delta);
Temperature currentTemperature() const;
private:
...
};
Новий клас ActiveTemperatureSensor став лише трішки складніший, але цілком адекватно виражає нову абстракцію. Створюючи екземпляр датчика, ми передаємо йому при ініціалізації не лише місце, але і вказівник на функцію зворотнього виклику, параметри якої визначають місце установки і температуру. Нова функція установки establishSetpoint дозволяє клієнтові змінювати поріг опрацьовування датчика температури, а відповідальність датчика полягає в тому, щоб викликати функцію зворотнього виклику кожного разу коли поточна температура actualTemperature відхиляється від setpoint більше, ніж на delta. При цьому клієнтові стає відомо місце спрацьовування датчика і температуру в ньому, а далі вже він сам повинен знати, що з цим робити.
Відмітимо, що клієнт як і раніше може запрошувати температуру за власною ініціативою. Але, що відбудеться, якщо клієнт не проведе ініціалізацію, наприклад, не задасть допустиму температуру? При проектуванні ми обов'язково повинні вирішити це питання, прийнявши яке-небудь розумне допущення: наприклад, нехай вважається, що інтервал допустимих змін температури нескінченно широкий.
Як саме клас ActiveTemperatureSensor виконує свої зобов'язання, залежить від його внутрішнього представлення і не повинно цікавити зовнішніх клієнтів. Це визначається реалізацією його закритої частини і функцій-членів.
Розглянемо тепер інший приклад абстракції. Для кожної вирощуваної культури має бути заданий план вирощування, що описує зміну в часі температури, освітлення, підгодівлі і ряду інших чинників, що забезпечують високий урожай. Оскільки такий план є частиною ПО, цілком виправдана його реалізація у вигляді абстракції.
Для кожної вирощуваної культури існує свій окремий план, однак загальна форма планів у всіх культур однакова. Основу плану вирощування складає таблиця, яка співставляє моментам часу перелік необхідних дій. Наприклад, для деякої культури на 15-ту добу зростання плану передбачано підтримку на протязі 16 годин температури 20С, з них 14 годин з освітленням, а потім пониження температури до 15С на решту часу доби. Крім того, може бути потрібне внесення добрив в середині дня, щоб підтримати задане значення кислотності.
Таким чином, план вирощування відповідає за координацію в часі всіх дій, необхідних під час вирощування культури. Наше рішення полягає в тому, щоб не доручати абстракції плану самому виконувати план, - це буде обов'язком іншої абстракції. Так ми зрозуміло розділимо поняття між різними частинами системи і обмежимо концептуальний розмір кожної окремої абстракції.
З точки зору інтерфейсу об'єкту-плану, клієнт повинен мати можливість встановлювати деталі плану, змінювати план і викликати його. Наприклад, об'єкт може бути реалізований з інтерфейсом "людина-комп’ютер" і ручною зміною плану. Об'єкт, який містить деталі плану вирощування, повинен вміти змінювати сам себе. Крім того, повинен існувати об'єкт-виконавець плану, що вміє читати план. Як видно з подальшого опису, жоден об'єкт не відособлений, а всі вони взаємодіють для забезпечення спільної мети. Виходячи з такого підходу, визначаються межі кожного об'єкту-абстракції і протоколи їх зв'язку.
На C++ план вирощування виглядатиме таким чином. Спочатку введемо нові типи даних, наближаючи наші абстракції до словника предметної області (день, година, освітлення, кислотність, концентрація):
// Число, що позначає день року
typedef unsigned int Day;
// Число, що позначає годину дня
typedef unsigned int Hour;
// Булевий тип
enum Lights {OFF, ON};
// Число, що позначає показник кислотності в діапазоні від 1 до 14
typedef float pH;
// Число, що позначає концентрацію в процентах: від 0 до 100
typedef float Concentration;
Далі, в тактичних цілях, опишемо наступну структуру:
// Структура, що позначає умови в теплиці
struct Condition {
Temperature temperature;
Lights lighting;
pH acidity;
Concentration concentration;
};
Ми використовували структуру, а не клас, оскільки Condition - це просто механічне об'єднання параметрів, без якої-небудь внутрішньої поведінки, і детальніша семантика класу тут не потрібна.
План вирощування:
class GrowingPlan (
public:
GrowingPlan (char *name);
virtual ~GrowingPlan();
void clear();
virtual void establish(Day, Hour, const Condition&);
const char* name() const;
const Condition& desiredConditions(Day, Hour) const;
protected:
...
};
Відмітимо, що ми передбачили один новий обов'язок: кожен план має ім'я, його можна встановлювати і викликати. Крім того операція establish описана як virtual для того, щоб підкласи могли її перевизначати.
У відкриту (public) частину опису винесені конструктор і деструктор об'єкту (визначальні процедури його породження і знищення), дві процедури модифікації (очищення всього плану clear і визначення елементів плану establish) і два селектора-визначники стану (функції name і desiredCondition). Ми опустили в описі закриту частину класу, замінивши її багатьма крапками, оскільки зараз нам важливі зовнішні посилання, а не внутрішнє подання класу.