- •Міністерство освіти та науки України в.В. Литвин, н.Б. Шаховська Проектування інформаційних систем
- •Передмова наукового редактора серії підручників «комп’ютинґ»
- •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.3. Інкапсуляція
Хоча ми описували нашу абстракцію GrowingPlan як співставлення діям моменти часу, вона не обов'язково має бути реалізована буквально як таблиця даних. Дійсно, клієнтові немає жодної справи до реалізації класу, який його обслуговує, до того часу, поки той дотримує свої зобов'язання. Насправді, абстракція об'єкту завжди випереджає його реалізацію. А після того, як рішення про реалізацію прийнято, воно повинне трактуватися як секрет абстракції, прихований від більшості клієнтів. Жодна частина складної системи не повинна залежати від якої-небудь внутрішньої іншої частини. В той час, як абстракція допомагає людям думати про те, що вони роблять", інкапсуляція "дозволяє легко перебудовувати програми.
Абстракція і інкапсуляція доповнюють одна одну: абстрагування направлене на спостереження поведінки об'єкту, а інкапсуляція займається внутрішнім облаштуванням. Найчастіше інкапсуляція виконується за допомогою приховування інформації, тобто маскуванням всіх внутрішніх деталей, що не впливають на зовнішню поведінку. Зазвичай ховається й внутрішня структура об'єкту та реалізація його методів.
Інкапсуляція, таким чином, визначає чіткі межі між різними абстракціями. Візьмемо для прикладу структуру рослини: щоб зрозуміти на верхньому рівні дію фотосинтезу, цілком можна ігнорувати такі деталі, як функції коріння рослини або хімія клітинних стінок.
Аналогічним чином під час проектування бази даних прийнято писати програми так, щоб вони не залежали від фізичного подання даних; замість цього зосереджуються на схемі, що відображає логічну будову даних. В обох випадках об'єкти захищені від деталей реалізації об'єктів нижчого рівня.
Абстракція працюватиме лише разом з інкапсуляцією. Практично це означає наявність двох частин в класі: інтерфейсу і реалізації. Інтерфейс відображає зовнішню поведінку об'єкту, описуючи абстракцію поведінки всіх об'єктів даного класу. Внутрішня реалізація описує представлення цієї абстракції і механізми досягнення бажаної поведінки об'єкту. Принцип розділення інтерфейсу і реалізації відповідає тому, що в інтерфейсній частині зібрано все, що стосується взаємодії об'єкту з будь-якими іншими об'єктами, а реалізація приховує від інших об'єктів всі деталі, що не мають відношення до процесу взаємодії об'єктів. Такі деталі називають "таємницями абстракції".
Отже, інкапсуляцію можна визначити таким чином:
Інкапсуляція - це процес відділення елементів об'єкта, що визначають його структуру та поведінку; інкапсуляція служить для того, щоб ізолювати контрактні зобов'язання абстракції від їхньої реалізації.
Приклади інкапсуляції. Повернемося до прикладу гідропонного тепличного господарства. Ще однією із ключових абстракцій цієї предметної області є нагрівач, що підтримує задану температуру в приміщенні. Нагрівач є абстракцією нижчого рівня, тому можна обмежитися лише трьома діями із цим об'єктом: включенням, вимкненням й запитом стану. Нагрівач не повинен відповідати за підтримку температури, це буде поведінкою вищого рівня, що спільно реалізується нагрівачем, датчиком температури й ще одним об'єктом. Ми говоримо про поведінку вищого рівня, тому що вона ґрунтується на простій поведінці нагрівача й датчика, додаючи до них ще гістерезис (або запізнювання), завдяки якому можна обійтися без частих включень і вимиканні нагрівача в станах, близьких до граничного. Прийнявши таке рішення про поділ відповідальності, кожна абстракція тоді має більшу ціль.
Як завжди, почнемо з типів.
// Булевий тип
enum Boolean {FALSE, TRUE};
На додаток до трьох запропонованих вище операцій, потрібні звичайні мета-операції створення й знищення об'єкта (конструктор і деструктор). Оскільки в системі може бути кілька нагрівачів, ми будемо при створенні кожного з них повідомляти його місце, де він встановлений, як ми робили це із класом датчиків температури TemperatureSensor. Отже, маємо клас Heater для абстрактних нагрівачів, написаний на C++:
class Heater {
public:
Heater(Location);
~Heater();
void turnOn();
void tum0ff();
Boolean is0n() const;
private:
};
От і все, що стороннім треба знати про клас Heater. Внутрішність класу це зовсім інша справа. Припустимо, проектувальники апаратури вирішили розмістити керуючі комп'ютери поза теплицею (де занадто жарко й волого), і з'єднати їх з датчиками й виконавчими пристроями за допомогою послідовних інтерфейсів. Розумно очікувати, що нагрівачі будуть комутуватися за допомогою блоку реле, а воно буде керуватися командами, що надходять через послідовний інтерфейс. Скажемо, для включення нагрівача передаються текстові імена команди, номер місця нагрівача й ще одне число, що використовується як сигнал включення нагрівача.
Ось клас, що виражає абстрактний послідовний порт.
class SerialPort { public:
SerialPort();
~SerialPort();
void write(char*);
void write(int);
static SerialPort ports[10];
private:
};
Екземпляри цього класу будуть послідовними портами, у які можна вводити рядки й числа.
Додамо ще три параметри в клас Heater.
class Heater {
public:
...
protected:
const Location repLocation;
Boolean repIsOn;
SerialPort* repPort;
};
Ці параметри repLocation, repIsOn, repPort утворять його інкапсульований стан. Правила C++ такі, що при компіляції програми, якщо клієнт спробує звернутися до цих параметрів прямо, буде видане повідомлення про помилку.
Визначимо тепер реалізації всіх операцій цього класу.
Heater::Heater(Location 1)
: repLocation(1),
repIsOn(FALSE),
repPort(&SerialPort::ports[l]) {}
Heater::Heater() {}
void Heater::turnOn()
{
if (!repls0n) {
repPort->write("*");
repPort->write(repLocation);
repPort->write(1);
repIsOn = TRUE;
}
}
void Heater::turn0ff()
{
if (repIsOn) {
repPort->write("*");
repPort->write(repLocation);
repPort->write(0);
repIsOn = FALSE;
}
}
Boolean Heater::is0n() const
{
return repIsOn;
}
Такий стиль реалізації типовий для добре структурованих об’єктно-орієнтованих систем: класи записуються економно, оскільки їхня спеціалізація здійснюється через підкласи.
Припустимо, що з якої-небудь причини змінилася архітектура апаратних засобів системи й замість послідовного порту керування повинне здійснюватися через фіксовану область пам'яті. Немає необхідності змінювати інтерфейсну частину класу - досить переписати реалізацію. Відповідно до правил C++, після цього прийдеться перекомпілювати змінений клас, але не інші об'єкти, якщо тільки вони не залежать від тимчасових і просторових характеристик попереднього коду (що вкрай небажано й зовсім не потрібно).
Звернемося тепер до реалізації класу GrowingPlan. Як було сказано, це, по суті, часовий графік дій. Ймовірно, кращою реалізацією його був би словник пар час-дія з відкритою хеш-таблицею. Нема сенсу запам'ятовувати всі дії в часі, вони відбуваються не так часто, а в проміжках між ними система може інтерполювати хід процесу.
Інкапсуляція приховує від сторонніх поглядів два секрети: те, що в дійсності графік використовує відкриту хеш-таблицю, і те, що проміжні значення інтерполюються. Клієнти думають, що вони одержують дані з погодинного масиву значень параметрів.
Розумна інкапсуляція локалізує ті особливості проекту, які можуть піддатися змінам. У міру розвитку системи розробники можуть вирішити, що якісь операції виконуються трохи довше, ніж має бути, а якісь об'єкти займають більше пам'яті, ніж прийнятно. В таких ситуаціях часто змінюють внутрішнє подання об'єкта, щоб реалізувати ефективніші алгоритми або оптимізувати алгоритм за критерієм пам'яті, замінюючи зберігання даних обчисленням. Важливою перевагою обмеження доступу є можливість внесення змін в об'єкт без зміни інших об'єктів.
В ідеальному випадку спроби звертання до даних, закритих для доступу, повинні виявлятися під час компіляції програми. Питання реалізації цих умов для конкретних мов програмування є предметом постійних обговорень. Так, Smalltalk забезпечує захист від прямого доступу до екземплярів іншого класу, виявляючи такі спроби під час компіляції. У теж час Object Pascal не інкапсулює подання класу, так що ніщо в цій мові не оберігає клієнта від прямих посилань на внутрішні поля іншого об'єкта. Мова CLOS займає в цьому питанні проміжну позицію, покладаючи всі обов'язки з обмеження доступу на програміста. У цій мові всі слоти можуть супроводжуватися атрибутами :reader, :writer і :accessor, що дозволяють відповідно читання, запис або повний доступ до даних (тобто й читання, і запис). При відсутності атрибутів слот повністю інкапсульований. У мові C++ керування доступом і видимістю гнучкіше. Екземпляри класу можуть бути віднесені до відкритих, закритих або захищених частин. Відкрита частина доступна для всіх об'єктів; закрита частина повністю закрита для інших об'єктів; захищена частина видна тільки екземплярам цього класу і його підкласам. Крім того, в C++ існує поняття "друзів" (friends), для яких відкрита закрита частина.
Приховування інформації - поняття відносне: те, що заховане на одному рівні абстракції, виявляється на іншому рівні. Зайти всередину об'єктів можна; правда, зазвичай потрібно, щоб розробник класу-сервера про це спеціально подбав, а розробники класів-клієнтів це зрозуміли. Інкапсуляція не рятує від помилок; вона захищає від помилок, але не від шахрайства. Зрозуміло, мова програмування тут взагалі ні при чому; хіба що операційна система може обмежити доступ до файлів, в яких описані реалізації класів. На практиці ж іноді просто необхідно ознайомитися з реалізацією класу, щоб зрозуміти його призначення, особливо, якщо немає зовнішньої документації.