Поиск

Полнотекстовый поиск:
Где искать:
везде
только в названии
только в тексте
Выводить:
описание
слова в тексте
только заголовок

Рекомендуем ознакомиться

'Контрольная работа'
Контрольная работа включает материал программы курса и состоит из письменных ответов (с рисунками и схемами) на 2 вопроса. В вариантах контрольных ра...полностью>>
'Документ'
Автор: В. А. Доморацкий Москва 2007 Рецензенты:доктор медицинских наук, кандидат психологических наук, профессор М.Н.Гордеев, заведующий кафедрой пси...полностью>>
'Реферат'
Бухгалтерський облік - це мова бізнесу. Якщо бізнес здійснюється у рамках окремо взятого підприємства, то дані бухгалтерського обліку використовуються...полностью>>
'Документ'
Этот материал написан по некогда очень известной книге П.Б. Ганушкина “Избранные труды”. Интересен он градацией личностей по комплексу психологически...полностью>>

№1: Концепція об’єктно-орієнтованого програмування. Об’єктна модель. 2

Главная > Документ
Сохрани ссылку в одной из сетей:

1

Смотреть полностью

68

Об’єктно-орієнтоване програмування

Зміст

Частина І

Основи об’єктно-орієнтованого програмування 2

Тема № 1: Концепція об’єктно-орієнтованого програмування. Об’єктна модель. 2

Тема № 2: Об’єктна модель. Складові об’єктного підходу. 6

Тема № 3: Класи та об’єкти. 16

Тема № 4: Процес проектування. 29

Частина ІІ

Об’єктно-орієнтоване програмування під ОС Windows 31

Тема № 5: Основи операційної системи Windows 31

Тема № 6 : Структура програм під ОС Windows 36

Тема № 7 : Бібліотека базових класів Microsoft (MFC) 40

Тема № 8: Структура програми на основі класів MFC 44

Тема № 9: Основні типи програм на основі класів MFC 51

Тема № 10: Елементи інтерфейсу користувача на основі класів MFC 57

Тема № 11: Графічні об’єкти в MFC 65

Контрольні запитання 68

До модулю 1. 68

До модулю 2. 68

Частина І

Основи об’єктно-орієнтованого програмування

Тема № 1: Концепція об’єктно-орієнтованого програмування. Об’єктна модель.

План

1. Складні системи. Декомпозиція, абстрагування та ієрархія.

2. Еволюція об’єктної моделі. Основи об’єктної моделі.

3. Об’єктно-орієнтовані програмування, проектування та аналіз.

Складні системи

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

Аспекти складності стосовно розробки програмного забезпечення:

- складність реального світу, або тої його частини яку моделює програма;

- складність управління процесом розробки;

- проблеми описання поведінки великих дискретних систем.

Структура складних систем

Основні властивості складних систем, що обґрунтовують об’єктний підхід:

- складні системи є ієрархічними і складаються із взаємопов’язаних частин, що також можуть ділитися на підсистеми;

- розділ систем на елементарні компоненти є відносно довільний і здебільшого залежить від дослідника;

- зв’язок в середині компонентів набагато сильніший ніж між компонентами, це обумовлює “високочастотну” взаємодію всередині компонентів та “низькочастотну” між компонентами;

- ієрархічні системи складаються з невеликої кількості типів підсистем різним чином скомбінованих та організованих;

- діюча складна система є результатом розвитку більш простої системи, що працювала раніше. (Складна система, що спроектована з “нуля” навряд чи запрацює.)

Досвід проектування складних систем показує [Буч], що найбільш успішними є ті системи в яких закладено добре продумані структури класів та об’єктів і які володіють згаданими вище властивостями.

Структури класів та об’єктів системи разом називають архітектурою системи.

При проектуванні складних систем розглядають три основні аспекти:

- декомпозиція;

- абстрагування;

- ієрархія.

Декомпозиція

На древньому принципі управління “розділяй та владарюй” і зараз базується побудова та управління складною системою. При проектування програмна система розкладається на все менші та менші підсистеми, що можуть розроблятися та вдосконалюватися незалежно.

До сих пір ми мали справу зі структурним проектуванням “зверху вниз” і декомпозиція сприймається як розділення алгоритмів, де кожен модуль виконує якусь частину загального процесу.

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

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

Абстракція

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

Ієрархія

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

Визначити ієрархічні зв’язки в системі не завжди просто, але після їх визначення структура складної системи та сама система стає зрозумілішою.

Зміст проектування

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

- задовольняє заданим (часто неформальним) специфікаціям функціонування;

- узгоджується з обмеженнями, що накладаються обладнанням;

- задовольняє явні та неявні вимоги з експлуатації та ресурсоспоживанню;

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

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

Методи проектування володіють деякими спільними властивостями:

- умовні позначення – мова для описання кожної моделі;

- процес правила проектування моделей;

- інструменти – засоби, які пришвидшують процес створення моделей, і в яких вже закладені закони функціонування моделей. Інструменти дозволяють виявити помилки в процесі розробки.

Еволюція об’єктної моделі

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

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

Перше покоління FORTRAN I, ALGOL 58 (математичні формули). Вони орієнтувалися в основному на інженерні та наукові розрахунки та підтримували виключно математичні абстракції.

Друге покоління FORTRAN II (підпрограми, роздільна компіляція), ALGOL 60 (блокова структура, типи даних), COBOL (описання даних, робота з файлами), Lisp (обробка списків, вказівними, збирання сміття). Основна тенденція цього покоління мов розвиток алгоритмічних абстракцій.

Третє покоління PL/1 (FORTRAN + ALGOL + COBOL), ALGOL 68 (строгий нащадок ALGOL 60), Pascal (спрощений нащадок ALGOL 60), Simula (класи, абстрагування даних) характеризується більш суттєвим кроком від підтримки машини до предметної галузі.

У четвертому поколінні було створено багато мов програмування високого рівня, але мало з них вижили. Але механізми, що були запропоновані ними втілювалися у нових версіях старіших мов. Саме тоді розвинулися об’єктні та об’єктно-орієнтовані мови Smalltalk (по новому розвинутий спадок від Simula). Ada (нащадок ALGOL 68 та Pascal з елементами Simula) CLOS (об’єднав Lisp, LOOPS та Flavors), C++ (гібрид C та Simula), Eiffel (об’єднання Simula та Ada). Ці мови можна назвати зараз об’єктно-орієнтованими.

Приклад (графічний). Структура об’єктно-орієнтованої програми представляється графом а не деревом, як у випадку процедурно-орієнтованої.

В основному об’єктно-орієнтований підхід пов’язаний з такими подіями як:

- значний прогрес в галузі архітектури ЕОМ;

- розвиток мов програмування, таких як Simula, Smalltalk, Ada;

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

- розвиток теорії баз даних;

- дослідження в галузі штучного інтелекту;

- досягнення філософії та теорії пізнання.

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

Об’єктно-орієнований аналіз та проектування визначають еволюційний, а не революційний розвиток проектування.

Основи об’єктної моделі

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

Розглянемо основні складові об’єктно-орієнований підхіду.

Об’єктно-орієнтоване програмування – методологія програмування, що ґрунтується на поданні програми у вигляді набору об’єктів, кожен з яких є екземпляром певного класу, а класи утворюють ієрархію наслідування [Буч].

Як випливає з означення, програма буде об’єктно-орієнованою тільки тоді, коли виконуються три основні вимоги: 1) як базові елементи використовуються об’єкти, а не алгоритми; 2) кожен об’єкт є екземпляром певного класу; 3) класи організовано ієрархічно.

Мову програмування можна назвати об’єктно-орієнованою тільки тоді, якщо у ній реалізовані механізми:

- підтримки об’єктів, тобто абстракції даних, що мають інтерфейс у вигляді іменованих операцій і власні дані з обмеженням доступу до них;

- об’єкти відносяться до відповідних типів;

- типи можуть успадковувати атрибути надтипів (надкласів).

За цією класифікацією до об’єктно-орієнованих мов програмування можна віднести Smalltalk, (чистий), C++, Object Pascal, CLOS, Java (гібридні), Ada (об’єктний).

Об’єктно-орієнтоване проектування – методологія проектування, що об’єднує в собі процес об’єктної декомпозиції та прийоми представлення логічної і фізичної, а також статичної і динамічної моделей системи, що проектується [Буч].

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

За результатами OOA формуються моделі, на яких ґрунтується OOD (проектування), OOD створює фундамент для реалізації системи з використанням методології OOP.

Тема № 2: Об’єктна модель. Складові об’єктного підходу.

План

1. Парадигми програмування. Парадигма об’єктно-орієнтованого стилю.

2. Основні складові частини об’єктно-орієнтованого стилю.

3. Додаткові складові частини об’єктно-орієнтованого стилю.

Парадигми програмування

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

Стиль програмування це спосіб побудови програм, що ґрунтується на певних принципах програмування та виборі відповідної мови, що робить зрозумілими програми які написані в цьому стилі [Бобров, Стетік].

На даний час вирізняються п’ять основних стилів програмування з відповідними їм абстракціями:

- процедурно-орієнтований (алгоритми);

- об’єктно-орієнтований (класи та об’єкти);

- логіко-орієнтований (цілі виражені в термінах обчислення предакатів);

- орієнтований на правила (правила “якщо-то”);

- орієнтований на обмеження (інваріантні відношення).

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

Парадигма об’єктно-орієнтованого стилю

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

- абстрагування;

- інкапсуляція;

- модульність;

- ієрархія.

Це основні елементи, тому що без наявності одного з них модель не можна буде назвати об’єктно-орієнтованою. Крім основних розрізняють ще три додаткових елементи:

- типізація;

- паралелізм;

- збережуваність.

Їх називаються додатковими через те що, вони корисні в об’єктно-орієнтованому стилю, але не є обов’язковими.

Основні складові частини об’єктно-орієнтованого стилю

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

Абстрагування

Зміст абстрагування. Абстрагування є одним з основних методів, що застосовується до рішення складних задач. Його основна задача відділити основне від дріб’язкового (зерно від полови).

Означення абстракції:

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

Вибір правильного набору абстракцій для заданої предметної області є основною задачею об’єктно-орієнтованого проектування.

Існує цілий ряд корисних абстракцій, що застосовуються для класифікації об’єктів реального світу:

- абстракція сутності (об’єкт є корисною моделлю деякої сутності в предметній області);

- абстракція поведінки (об’єкт складається з узагальненої множини операцій);

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

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

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

Для глибшого розуміння складових об’єктно-орієнтованого програмування вводиться поняття контрактної моделі [Мейер]. Зовнішня поведінка об’єкту розглядається з точки зору його контракту з іншими об’єктами, відповідно до цього повинна реалізовуватися його внутрішня структура (часто у взаємодії з іншими об’єктами). Наприклад, контракт фіксує всі зобов’язання об’єкту-серверу (надає послуги) перед об’єктом-клієнтом (користується послугами). Цей контракт визначає відповідальність об’єкту за ту поведінку, яка за ним закріплена.

Кожна операція, що передбачена цим контрактом, однозначно визначена її формальними параметрами та типом значення, що повертається. Повний набір операцій, які клієнт може виконувати над об’єктом-сервером, разом з правильним порядком їх застосування – називається протоколом. Протокол визначає всі можливі способи, якими об’єкт може діяти або піддаватися впливу, тобто визначає статичну та динамічну поведінку абстракції.

Центральною ідеєю абстракції є поняття інваріанту. Інваріант – деяка логічна умова, значення якої (істина чи хиба) повинна зберігатися. Для кожної операції об’єкту можна задавати передумови (інваріанти, що допускаються операцією) і посляумови (інваріанти, яким задовольняє операція).

Усі абстракції володіють як статичними так і динамічними властивостями. Наприклад, файл як об’єкт вимагає певного об’єму на носії. Має ім’я та вміст (статичні атрибути). В той же час, значення кожної з приведених властивостей може бути змінено в процесі використання файлу.

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

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

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

Розглянемо тепер властивості давача як такого. Які у нього обов’язки перед клієнтом? На певному рівні абстракції давач повинен знати своє положення та біжуче значення величини яку він вимірює. Які ж дії може виконувати клієнт по відношенню до давача? На примітивному рівні, клієнт може калібрувати давач та отримувати від нього біжуче значення величини, яку той вимірює.

Проілюструємо сказане вище з використанням мови програмування С++.

// Значення величини, що вимірюється

typedef float Value;

// Число, що однозначно визначає положення давача

typedef unsigned int Location;

class Sensor {

public:

Sensor(Location);

~Sensor();

void calibrate(Value actualValue);

Value currentValue() const;

private:

. . .

};

Клас Sensor – це специфікація давача, а його начинка схована в закритій частині (за ключовим словом private). Описаний клас ще не є об’єктом. Власне об’єкти – це екземпляри класу, і їх необхідно створити. Наприклад, засобами мови С++ це записується:

Value value;

Sensor SensorFirst(1);

Sensor SensorSecond(2);

Value = SensorFirst.currentValue();

. . .

Розглянемо інваріанти, що пов’язані з операцією currentValue. Передумова виражає допущення, що давач розміщено у відповідному місці теплиці, а постумова – що давач видає дійсне значення у відповідних одиницях.

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

Розглянутий давач відноситься до класу пасивних давачів. Для того, щоб отримати значення величини, що вимірюється необхідно опитати його. Іншим прикладом абстракції може бути активний давач, який за певних умов (час, відхилення від заданої величини) сам повідомляє клієнту про свій стан. У цьому випадку тільки дещо змінюється відповідальність об’єкту. Для рішення цієї проблеми є зворотній виклик (callback). Клієнт надає серверу функцію (функцію зворотного виклику), а сервер викликає її при необхідності. Код у цьому випадку запишеться:

class ActiveSensor {

public:

ActiveSensor(Location, void (*f)(Location, Value));

~ ActiveSensor();

void calibrate(Value actualValue);

void establishSetPoint(Value setPoint, Value Delta);

Value currentValue() const;

private:

. . .

};

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

Інкапсуляція

Сутність інкапсуляції. При розгляді абстракції описувалася тільки поведінка об’єкту, тобто укладалися контрактні умови, що повинен виконувати об’єкт і як клієнт може взаємодіяти з об’єктом. В загальному клієнта не цікавить яким чином сервер організовано і яких зусиль він (сервер) докладає, щоб виконати умови контракту (зобов’язання) зі своєї сторони. Ніяка частина складної системи не повинна залежати від внутрішньої будови якої-небудь іншої частини [Інглас]. Абстракція допомагає обдумувати те, що робиш, а інкапсуляція дозволяє легко перебудовувати програми.

Означення інкапсуляції:

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

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

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

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

// Булевий тип

enum Boolean {FALSE, TRUE};

class Heater {

public:

Heater(Location);

~ Heater();

void turnOn();

void turnOff();

Boolean isOn() const;

private:

. . .

};

Нижче ключового слова public описано інтерфейс класу, все що необхідно користувачу знати про об’єкт.

Якщо, нагрівач буде керуватися із-зовні теплиці через послідовний комунікаційний порт, то необхідно добавити клас, що його реалізує:

class SerialPort {

public:

SerialPort(Location);

~ SerialPort();

void write(char*);

void write(int);

static SerialPort port[10];

private:

. . .

};

Тоді захищена частина класу Heater матиме вигляд:

class Heater {

public:

. . .

protected:

const Location repLocation;

Boolean repIsOn;

SerialPort* repPort;

};

Змінні repLocation, repIsOn, repPort утворюють інкапсульований стан об’єкту. До них обмежене пряме звертання з інших об’єктів (компілятор генеруватиме помилку).

Методи (або функції-члени) можна реалізувати наступним чином:

Heater::Heater(Location loc) : repLocation (loc),

repIsOn(FALSE),

repPort(&SerialPort::ports(1))

{ }

Конструктор за замовчуванням:

Heater::Heater() { }

void Heater::turnOn()

{

if (!repIsOn) {

repPort->write(“*”);

repPort->write(repLocation);

repPort->write(1);

repIsOn = TRUE;

}

}

void Heater::turnOff()

{

if (repIsOn) {

repPort->write(“*”);

repPort->write(repLocation);

repPort->write(0);

repIsOn = FALSE;

}

}

Boolean Heater::isOn() const { return repIsOn; }

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

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

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

Модульність

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

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

Різні дослідники характеризують її з різними відтінками. “Модульність – це поділ програми на фрагменти, що окремо компілюються, але можуть встановлювати зв’язки з іншими модулями” [Лесков]. “Зв’язки між модулями – це їх уява один про одного” [Парнас].

В різних мовах програмування механізм модульності реалізовано по різному. Інтуїтивний в С++, інтерфейс в заголовкових файлах (*.h), а реалізація у файлах програм (*.cpp) і зв’язок між модулями реалізовується за допомогою директиви #include. В Ada та Object Pascal є спеціальні ключові слова для визначення інтерфейсу модулю та його реалізації.

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

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

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

За означенням:

Модульність – це властивість системи, яка була розкладена на внутрішньо зв’язні, але слабо зв’язані між собою модулі.

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

При колективній розробці програмного забезпечення роботу розподіляють за модульним принципом. Правильний поділ системи на модулі мінімізує зв’язки між працівниками. Це особливо актуально коли програма розробляється різними групами чи підприємствами.

Приклади модульності. Засобами мови С++ інтерфейс модулю, що реалізує теплицю може бути описаний в фалі-заголовку (gplan.h) так:

// gplan.h

#ifndef GPLAN_H

#define GPLAN_H 1

#include “gtypes.h”

#include “except.h”

#include “actions.h”

class GrowingPlan . . .

class FruitGrowingPlan . . .

class GrainGrowingPlan . . .

. . .

#endif

В модулі зібрані базовий та похідні від нього класи планів вирощування фруктів та овочів, які очевидно мають сильні зв’язки між собою і слабо зв’язані з іншими бібліотеками, наприклад графічного інтерфейсу користувача за допомогою яких розроблятиметься оболонка програми. Для інших модулів класи, що визначають нагрівачі та давачі невидимі і ніби не існують.

Ієрархія

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

Ієрархія – це впорядкування абстракцій, розклад їх за рівнями.

Основними видами ієрархічних структур є структура класів (ієрархія “is-a” [“є”]) та структура об’єктів (ієрархія “part of” [“частина ...”])

Приклади ієрархії: одиночне наслідування. Наслідування визначає між класами відношення типу “батько/нащадок”. Коли один об’єкт переймає (успадковує) від іншого (або декількох інших) об’єкту структуру та функціональні властивості. Часто підклас (нащадок) добудовує або перебудовує компоненти надкласу (батьківського).

Наприклад. Вовк – хижак – ссавець. Капуста – городина – рослина. Суматор – цифровий пристрій – мікросхема. Наслідування породжує ієрархію “узагальнення - спеціалізація”, де класи нащадки уточнюють деякі властивості батьківських класів.

Для прикладу тепличної системи із загального плану вирощування можна отримати план культивації фруктів:

// Тип урожай

typedef unsigned int Yield;

class FruitGrowingPlan : public GrowingPlan {

public:

FruitGrowingPlan(char *name);

virtual ~ FruitGrowingPlan();

virtual void establish(Day, Hour, Condition&);

void scheduleHarvest(Day, Hour);

Boolean isHarvest() const;

unsigned daysUntilHarvest() const;

Yield estimatedYield() const;

protected:

Boolean repHarvested;

Yield repYield;

};

Ця запис значить, що план FruitGrowingPlan є різновидом плану GrowingPlan. Продовжуючи спеціалізацію можна створити клас для вирощування яблук AppleGrowingPlan на основі плану вирощування фруктів.

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

Принципи абстрагування, інкапсуляції та ієрархії перебувають між собою в здоровому конфлікті. Інкапсуляція скриває реалізацію об’єкту а наслідування вимагає забезпечення доступу до стану та функцій об’єкту праотця об’єктам нащадкам (навіть через декілька рівнів). Найбільшу гнучкість у цьому відношення забезпечується мовою програмування С++, що володіє трьома рівнями захисту інтерфейсу: закритим (private); захищеним (protected); та повністю відкритим (public).

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

Множинне наслідування породжує значні проблем та неоднозначності при реалізації мов програмування, коли виникають конфлікти через перехресне наслідування - базові класи є нащадками (можливо в різних колінах) одного і того ж класу. Або в обох класах є члени з однаковими іменами. Такі конфлікти спричиняють помилки в процесі компіляції і в С++ їх треба розв’язувати вручну.

Приклади ієрархії: агрегація. Ієрархія агрегації вводиться через відношення “part of”. Агрегація присутня у всіх мовах, що підтримують групування різнотипних даних через “структури” та “записи”. Стосовно об’єктно-орієнтованого програмування агрегація дозволяє згрупувати логічно-зв’язані структури класів.

Наприклад процесор складається різнотипних але тісно зв’язаних поміж собою вузлів: регістрів; суматорів; перемножувачів; пам’яті і ін.

Типізація

Поняття типізації. Тип – точна характеристика властивостей, що включає структуру та поведінку та відноситься до деякої сукупності об’єктів [Дойч]. Можна вважати, тип та клас це однокові поняття, але в деяких мовах вони розрізняються.

Типізація – це спосіб захиститися від використання об’єктів одного класу замість іншого, або в крайньому випадку керувати таким використанням.

Сильна та слаба типізація. Мова С++ відноситься до мови зі слабим контролем типів (хоча й спостерігається тяга до їх строгого контролю) на відміну від мов Pascal та Ada, які строго контролюють типи.

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

Наприклад, існує ієрархія ємностей:

class Tank { . . . };

class WaterTank : public Tank { . . . };

class GasTank : public Tank { . . . };

З позицій типізації об’єктно-орієнтованого програмування класи WaterTank та GasTank визначають різні типи, які безумовно володіють деякими оригінальними властивостями. Оскільки вони породженні від одного класу Tank, то мають і спільні властивості. Можна створювати функції, що однаково будуть обробляти об’єкти цих різних типів, що дозволяє досягнути значної гнучкості програми (узгодження за типами досягається шляхом їх приведення), але ці функції будуть коректно працювати тільки на рівні властивостей базового типу. Використання приведення типів з подальшим застосуванням оригінальних членів класу може привести до помилок в процесі виконання. Безпомилкове присвоювання можна застосовувати тільки знизу вверх за ієрархією.

Строгий контроль типів має наступні переваги [Теслер]:

- відсутність контролю типів приводить до загадкових збоїв у процесі виконання програм;

- у більшості випадків процес редагування – компіляція – відлагодження значно ускладнює програмування, і раннє виявлення помилок просто не заміниме;

- оголошення типів покращує документування програми;

- багато компіляторів генерує більш ефективний об’єктний код, коли їм явно відомі типи.

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

Приклади типізації: статичне та динамічне зв’язування. Зв’язування визначає час коли імена об’єктів ставляться у відповідність типам. Статичне (або раннє) зв’язування означає, що типи всіх змінних та виразів відомі на час компіляції. Динамічне (або пізнє) зв’язування означає, що типи невідомі до часу виконання програми. Концепції типізації та зв’язування є незалежними. В різних мовах вони зустрічається в різних комбінаціях: типізація – сильна, зв’язування – статичне (Ada); типізація – сильна, зв’язування – динамічне (C++, Object Pascal); типи – відсутні, зв’язування – динамічне (Smalltalk).

Паралелізм

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

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

Об’єктно-орієнтована модель добре підходить для розподілених систем, так як вона неявно розбиває програму на (1) розподілені одиниці та (2) зв’язні суб’єкти [Блек].

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

Паралелізм – це властивість, що відрізняє активні об’єкти від пасивних.

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

В об’єктно-орієнтованому програмуванні спостерігаються три підходи до паралелізму.

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

По-друге, можна використовувати бібліотеку класів, що реалізують який-небудь різновид “легкого” паралелізму (MFC).

По-третє, можна створити ілюзію паралелізму через систему переривань.

При використанні паралелізму ключовим питанням є синхронізація потоків та розподіл доступу до спільних ресурсів.

Збережувансть

Поняття збережуваності. Спектр збережуваності об’єктів охоплює:

- проміжні результати обчислення виразів;

- локальні змінні при виклику функцій;

- власні змінні, глобальні змінні та дані, що створюються динамічно;

- дані, що зберігаються між сеансами виконання програми;

- дані, що зберігаються при переході до нової версії програми;

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

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

Збережуваність – це не тільки проблема збереження даних. В об’єктно-орієнтованих базах даних (ООБД) є зміст зберігати класи, так щоб розробники (програмісти) могли правильно інтерпретувати ці дані.

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

Збережуваність можна визначити як:

Збережуваність – властивість об’єкту існувати в часі, переживаючи процес, що його створив, та (або) в просторі, переміщаючись їі свого первинного адресного простору.

Тема № 3: Класи та об’єкти.

План

1. Поняття об’єкту. Характеристики об’єкту. Відношення між об’єктами.

2. Поняття класу. Характеристики класу. Відношення між класами.

3. Відношення між класами та об’єктами. Якість класів та об’єктів.

Поняття об’єкту

З точки зору системи сприйняття людини, об’єктом може бути:

- видимий предмет, або той, що сприймається відчуттям;

- дещо, що сприймається мисленням;

- дещо, на що направлена думка або дія.

Таким чином об’єкт моделює частину реальної дійсності, що нам оточує, тобто він існує в просторі та часі. Термін об’єкт вперше введено в мові Simula і застосовувався для моделювання реальності [1].

З точки зору деяких дослідників [Сміт, Токі] “Об’єкт визначає конкретний предмет, що розпізнається, одиницю чи сутність (реальну чи абстрактну), що має чітко визначене функціональне призначення в даній предметній області”. Інакше кажучи, чітко означені межі.

До об’єктів не можна віднести такі атрибути як краса, колір, емоції, час. Але перераховане - є властивостями об’єктів.

На основі розглянутого об’єкту можна дати наступне означення.

Об’єкт володіє станом, поведінкою та ідентичністю; структура та поведінка подібних об’єктів визначає загальний для них клас; терміни “екземпляр класу” та “об’єкт” взаємозамінні.

Розглянемо основні характеристики об’єктів.

Стан.

Поведінка об’єкту визначається його передісторією: важлива послідовність дій, що виконуються над об’єктом. Така залежність поведінки об’єкту від від подій та від часу пояснюється тим, що об’єктів є свій внутрішній стан. Тому можна вважати, що:

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

(приклад автомату, що торгує напоями)

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

Приклади. Реєстраційні записи про працівників.

class PersonalRecord {

public:

char *employeeName() const;

int employeeSocialSecurityNumber() const;

char *employeeDepartment() const;

protected:

char name[100];

int socialSecurityNumber;

char department[10];

float salary;

};

Можливі стани приведеного об’єкту визначається захищеними змінними.

Поведінка.

Так як об’єкти існують не ізольовано, піддаються впливу інших об’єктів або самі на них впливають, то:

Поведінка – це те, як об’єкт діє та реагує; поведінка виражається в термінах стану об’єкту та передачі повідомлень.

Інакше кажучи, поведінка об’єкту – це діяльність, що спостерігається та провіряється ззовні.

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

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

Стан об’єкту є сумарним результатом його поведінки.

Приклад. Чергу засобами С++ можна описати так.

class Queue {

public:

Queue ();

Queue (const Queue&);

virtual ~Queue ();

virtual Queue& operator = (const Queue&);

virtual int Queue& operator == (const Queue&) const;

int Queue& operator != (const Queue&) const;

virtual void clear();

virtual void append(const void&);

virtual void pop();

virtual void remove(int at);

virtual int length() const;

virtual int isEmpty() const;

virtual const void * front() const;

virtual int location(const void *) const;

protected:

. . .

};

Для створення об’єктів необхідно записати наступне:

Queue a, b, c, d;

Тепер над об’єктами можна виконувати операції.

a.append(&deb);

a.append(&karen);

a.append(&denise);

b = a;

a.pop();

Операції. Операція – це послуга, яку клас може надати своїм клієнтам. На практиці найбільш поширені п’ять типів операцій. Перші три:

модифікатор – змінює стан об’єкту;

селектор – зчитує стан об’єкту але не змінює його (стану);

ітератор – дозволяє отримати доступ до всіх частин об’єкту в певній послідовності.

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

конструктор – створення об’єкту та його ініціалізація;

деструктор – звільнює стан об’єкту або знищує сам об’єкт.

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

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

Об’єкти можуть бути активними та пасивними, володіти або ні власним потоком керування (паралелізм). Активний може бути автономним, міняти свій стан без зовнішнього впливу.

Ідентичність.

Визначення за Хошафяном та Коуплендом:

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

Джерелом багатьох помилок в об’єктно-орієнтованому програмування є невміння розрізняти ім’я об’єкту від власне самого об’єкту. Часто путають адресу об’єкту та ідентичність. Нехай ми маємо якийсь клас DisplayItem. Створимо його екземпляри:

DisplayItem;

DisplayItem* item2 = new DisplayItem( 75, 75);

DisplayItem* item3 = new DisplayItem(100, 100);

DisplayItem* item4 = 0;

Після такого оголошення у пам’яті виникає чотири імені та три об’єкти. item1 – ім’я об’єкту, а item2 та item3 - вказівники на об’єкти. Якщо застосувати операції

item1.move(item2->Location());

item4 = item3;

item4->move(Poinr(38, 100));

то об’єкти item1 та item2 матимуть однаковий стан, але це різні об’єкти, в той же час item3 та item4 вказують на один і той же об’єкт і переміщення буде застосоване до третього об’єкту. item3 та item4 є синонімами одного об’єкту. Присвоєння

item2 = &item1;

призводить до втрати контролю на д другим об’єктом, що в свою чергу приводить до втрати пам’яті. В деяких мовах програмування (Smalltalk, Java) є збирачі сміття, що повернуть втрачену пам’ять системі, а інших (С++, Object Pascal, Ada) пам’ять не звільниться до завершення програми, що є причиною нестабільності роботи, а часто і зависання програми.

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

Семантика С++ дозволяє керувати процесом копіювання об’єктів, тобто визначати, чи об’єкти передаватимуться через посилання, чи через повну копію. Для копіювання в класі створюється спеціальний копіюючий конструктор. Він може бути викликаний явно (як частина описання об’єкту) або неявно (при передачі об’єкту за значенням). При відсутності такого конструктора викликається конструктор за замовчування, що здійснює по-елементне копіювання об’єкту. Якщо, в об’єкт входять вказівники на інші об’єкти, то вводиться поняття “глибокого” копіювання, рекурсивно копіюються всі об’єкти на які існують посилання.

Присвоювання – це в принципі копіювання, його семантику в С++ також можна перевизначати, забезпечуючи “поверхневе” чи “глибоке” копіювання.

При порівнянні поняття “поверхневе” чи “глибоке” мають таке ж значення, як і при присвоюванні.

Час життя об’єктів. Початком життя об’єкту вважають його момент створення (виділення ділянки пам’яті), а кінцем – повернення відведеної ділянки операційній системі.

Об’єкти створюються явно та неявно. Явно створюються: при оголошенні об’єкт розміщується в стеку (item1); при використанні оператора new, пам’ять під об’єкти виділяється з “купи” (item2 та item3).

Неявно створення об’єкту відбувається коли він передається функції за значенням і С++ створює тимчасову його копію в стеку.

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

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

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

Зв’язки.

Зв’язок – це специфічне співставлення, через яке клієнт запитує послугу в об’єкту-серверу або через яке один об’єкт знаходить шлях до іншого.

a:DisplayItem

aControler aView

b:DisplayItem

Рис. Зв’язки

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

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

Сервер – об’єкт може тільки піддаватися впливу зі сторони інших об’єктів, але він ніколи не виступає в ролі об’єкту, що впливає на інші (b:DisplayItem).

Агент – об’єкт може виступати як в активній так і в пасивній ролі; як правило об’єкт-агент створюється для виконання операцій в інтересах якогось об’єкту-актору або агенту (a:DisplayItem, aView).

Приклад. (Процес нагрівання, підтримування певної температури)

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

- сервер глобальний у відношенні до клієнта;

- сервер (або вказівник на нього) переданий клієнту як параметр операції;

- сервер є частиною клієнта;

- сервер локально породжується клієнтом в ході якоїсь операції.

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

- послідовний – семантика пасивного об’єкту забезпечується присутністю тільки одного активного процесу;

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

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

Агрегація.

На відміну від зв’язків, що забезпечують рівноправні або “клієнт-сервер” відношення між об’єктами, агрегація описує відношення цілого та частини, що є відповідним типом ієрархії об’єктів. Виходячи від цілого (агрегати), приходимо до його частин (атрибутів). Тобто, агрегація частковий випадок асоціації.

growingRamp

rampControler

h:Heater

Рис. Агрегація

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

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

Поняття класу

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

У контексті об’єктно-орієнтованого програмування:

Клас – це деяка множина об’єктів, що мають подібну структуру та подібну поведінку.

За своєю природою клас – це генеральний контракт між абстракцією та її клієнтами. Об’єкт, про що йшлося раніше є екземпляром певного класу. На достатньо високому рівні абстракції ми здебільшого маємо справу з об’єктами, а не з класами. Якщо описувати ці ріні як класи, то можна отримати громіздку, мало придатну для повторного використання (унікальну) структуру.

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

Інтерфейс ділиться на три частини:

- відкриту (public) – що видима для всіх клієнтів;

- захищену (protected) – що видима тільки в самому класі, його підкласам (нащадкам) та друзям;

- закриту (private) – що видима тільки класу та його друзям.

В С++ інтерфейс описується в файлах заголовків (*.h), а реалізація в файлах програм (*.cpp).

Відношення між класами

Відомі три основних відношення між класами: “узагальнення / спеціалізація” (відношення “is a”); “ціле / частина” (відношення “part of”); семантичні, змістовні відношення, асоціації. Виходячи з цього більшість об’єктно-орієнтованих мов підтримують різні комбінації наступних відношень:

- асоціація;

- наслідування;

- агрегація;

- використання;

- інстанціювання;

- метаклас.

Асоціація

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

Приклад товарів та продаж.

Під потужністю відношення типу асоціація розуміють кількість учасників відношення. Найчастіше зустрічаються три схеми:

- “один-до-одного”;

- “один-до-багатьох”;

- “бато-до-багатьох”.

В класах асоціації визначаються, через оголошення взаємних вказівників один на одного.

class Product;

class Sale;

class Product {

. . .

protected:

Sale * lastSale;

. . .

};

class Sale {

. . .

protected:

Product ** productSold;

. . .

};

У даному прикладі відображено зв’язок “один-до-багатьох”.

Наслідування

Наслідування – це відношення між класами, коли клас повторює структуру та поведінку іншого класу (одиночне наслідування) або інших класів (множинне наслідування).

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

Механізм наслідування відрізняє об’єктно-орієнтовані мови від об’єктних.

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

class TelemetryData {

public:

TelemetryData();

virtual ~TelemetryData();

virtual void transmit();

Time currentTime();

protected:

int id;

Time timeStamp;

};

class ElectricalData : public TelemetryData {

public:

ElectricalData(float v1, float v2, float a1, float a2);

virtual ~ ElectricalData();

virtual void transmit();

float currentPower() const;

protected:

float fuelCeill1Voltage, fuelCeill2Voltage;

float fuelCeill1Amperes, fuelCeill2Amperes;

};

Класи, екземпляри яких не створюються називаються абстрактними класами. В С++ абстрактний клас визначається через наявність оголошених але не реалізованих віртуальних функцій. Доки така функція не буде реалізована компілятор не дозволить створити екземпляру цього класу.

void TelemetryData::transmit()

{

// передати id

// передати timeStamp

}

void ElectricalData::transmit()

{

TelemetryData::transmit();

// передати напругу

// передати силу струму

}

В С++ класи мають деревоподібну структуру (ліс). В інших мовах програмування (Smalltalk, Java) існує базовий клас від якого походять усі інші класи.

Поведінка підкласу може перевизначитися через зміни в реалізації методів. В Smalltalk перевизначитися може довільний метод. В С++, тільки ті методи, що позначені ключовим словом virtual, а інші ні.

Одиночний поліморфізм. Дозволяє розрізняти оператори та операції в залежності від кількості параметрів та їх типів. Стосовно до операторів така властивість називається перевантаженням оператора. Найбільш гнучко поліморфізм реалізовано в мові С++. Поліморфізм дозволяє обійтися без операторів вибору так як об’єкти самі знають свій тип.

void transmitFreshData(TelemetryData & d, const Time & t)

{

if (d. currentTime() >= t) d. transmit();

}

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

Наслідування та типізація. В більшості об’єктно-орієнтованих мов програмування при реалізації методів підкласу є механізм виклику методів надкласу напряму. В С++ для цього використовуються кваліфікатори, якими є імена відповідних надкласів. Дозволяється звертатися до довільного рівня вкладеності. Об’єкт може посилатися сам на себе за допомогою вказівника this. Методи надкласів викликаються до або після уточнюючих дій (поведінки) підкласу.

Реалізований в С++ механізм типізації дозволяє застосовувати наступне присвоювання.

TelemetryData telemetry;

ElectricalData electrical;

telemetry = electrical;

Але таке присвоювання небезпечне, бо довільні дані, що є розширеннями підкласу (чотири додаткових змінні-члени) втрачаються. Зворотне присвоювання недопустиме, так як telemetry не є підтипом electrical.

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

При застосування множинного наслідування виникають дві основні проблеми це розв’язання конфліктів між надкласами (конфлікт імен) та повторне наслідування.

Для подолання конфлікту імен застосовують три способи. Перший, одинакові імена вважаються помилками і код відповідно коректується (Smalltalk, Eiffel). Другий, одинакові імена вважаються одним атрибутом (CLOS). Третій (найбільш гнучкий), використовуються префікси (кваліфікатори) С++.

Проблема повторного наслідування також розв’язується трьома способами. Перший, повторне наслідування забороняється на етапі компілювання (Smalltalk, Eiffel). Другий, явно розводять класи джерела через систему префіксів (С++). Третій, множинні посилання розглядають як посилання на один і той же клас (С++).

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

Агрегація

Відношення агрегації між класами та об’єктами тісно пов’язані між собою. З першого випливає друге.

Розглянемо приклад:

class TemperatureController {

public:

TemperatureController(Location);

~TemperatureController();

void process(const TemperatureRamp &);

Minute schedule(const TemperatureRamp &) const;

protected:

Heater h;

};

Об’єкт класу Heater агрегований в клас TemperatureController.

В об’єктно-орієнтованому програмування розрізняють два види агрегації:

- за значенням, фізичне включення;

- за посиланням.

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

Агрегацію за посиланням часто можна переплутати з асоціацією. Тут слід пам’ятати, що асоціація двох-направлений зв’язок, а агрегація одно-направлений і виражає відношення “цілого/частини”. Тобто, один з об’єктів повинен бути фізичною чи логічною (акціонер та акції) частиною іншого.

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

Рис. Агрегація

Використання

У наведеному вище прикладі клас TemperatureController використовує клас TemperatureRamp, що передається його методам як параметр. Це типовий приклад використання.

Відношення використання між класами відповідає рівноправному зв’язку між їх екземплярами. У використання, перетворюється асоціація, якщо об’єкти є у відношенні “клієнт-сервер”.

Використання може також реалізовуватися при оголошенні екземплярів інших класів як локальні змінні методів цього класу.

Рис. Відношення використання

Інстанціонування

Відношення інстанціонування реалізовується через механізм параметризованих класів, що підтримується в С++ та 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 Item *) const;

protected:

. . .

};

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

Параметризований клас не може мати екземплярів доки не буде інстанційованим. Наприклад.

Queue<int> intQueue;

Queue<DisplayItem*> itemQueue;

Створені об’єкти intQueue та itemQueue – екземпляри зовсім різних класів, вони навіть не мають спільного надкласу. Але вони отримані з одного параметризованого класу Queue.

Інстанціонування безпечне з точки зору контролю типів. При даній реалізації компілятор вже може строго проконтролювати типи елементів, що включаються та вибираються з черги. При попередній реалізації черги через ідіому void* про контроль типів не можна було б навіть вести мови.

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

Рис. Інстанціонування

Метакласи

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

В С++ метакласів нема, але семантика його конструкторів, деструкторів, статичних змінних та методів дозволяє реалізувати поведінку класів до тої, що забезпечується мета класами в чистих об’єктно-орієнтованих мовах (Smalltalk, CLOS).

Відношення між класами та об’єктами

Класи та об’єкти – це окремі, але тісно пов’язані між собою поняття. Кожен об’єкт є екземпляром деякого класу, а з класу може бути породжено декілька об’єктів. Класи, це статичні категорії, використовуються на етапі компіляції, а об’єкти створюються та знищуються в процесі виконання програми.

На етапі аналізу та початкових стадіях проектування вирішуються дві основні задачі:

- виявлення класів та об’єктів, що складають словник предметної області (ключові абстракції, структура класів та об’єктів);

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

Якість класів та об’єктів

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

Для оцінки якості класів та об’єктів, що виділяються в системі, пропонуються п’ять критеріїв:

- зачеплення – це ступінь глибини зв’язків між окремими модулями, а також класами та об’єктами. Складність системи може бути зменшена за рахунок зменшення зачеплення між відповідними категоріями (модулями, класами та об’єктами). Є певне протиріччя між зачепленням та наслідуванням (зменшення зачеплення – подібність абстракцій).

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

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

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

- примітивність – в класі переважають операції, що потребують доступу до внутрішньої реалізації абстракцій.

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

Для прийняття рішення в якому з класів реалізовувати певну поведінку використовуються наступні міркування:

- повторне використання (Чи буде ця поведінка корисна в більш ніж одному контексті?);

- складність (Наскільки важко реалізувати таку поведінку?);

- застосування (Наскільки дана поведінка характерна для класу, в який вона включається?);

- знання реалізації (Чи потрібно для реалізації даної поведінки знати таємниці класу?).

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

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

- синхронна (операція виконується тільки при одночасній готовності об’єктів, що його передає та приймає, очікування готовності може бути безмежно довгим);

- з врахуванням затримки (так як і синхронна, але коли об’єкт, що приймає повідомлення не готовий, то операція не виконується);

- з обмеженням за часом (так як і синхронна, але об’єкт, що посилає повідомлення буде чекати готовності іншого об’єкту, тільки певний час);

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

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

Можна розглянути два діагональні підходи до проектування структури класів, між якими необхідно знайти розумний компроміс. Ліс (багато окремих дерев) – класи можуть вільно змішуватися та вступати у взаємовідносини. Одне дерево з гілками, що виходять від одного предка. Кожен з підходів має свої переваги та недоліки. В лісі неефективно використовується можливості узагальнення/спеціалізації. В дереві важко зрозуміти класи без детального аналізу контексту всіх їх предків.

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

Видимість об’єктів забезпечується чотирма основними способами:

- сервер є глобальним;

- сервер передається клієнту як параметр операції;

- сервер є частиною клієнта в змісті класів;

- сервер локально оголошується в області видимості клієнта.

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

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

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

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

У будь-якому випадку вибір представлення не повинен впливати на інтерфейс класу.

Модульна структура. При поділі програми на модулі проектувальник повинен розв’язати компроміс між вимогами видимості класів та об’єктів і ховання інформації. В загальному випадку модулі повинні бути функціонально сильно зв’язані із середини і слабо зв’язані один з одним. При цьому необхідно враховувати такі фактори як: повторне використання; безпека; придатність до документування.

Тема № 4: Процес проектування.

План

1. Складові частини процесу проектування.

2. Мікро-процес об’єктно-орієнтованого проектування.

3. Макро-процес об’єктно-орієнтованого проектування.

Процес проектування

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

Початковий – процес проектування здебільшого невпорядкований хаотичний.

Відтворюваний – організація в деякій мірі керує своїми планами та зобов’язаннями.

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

Керований – вироблені кількісні показники процесу. Наступна мета – зменшити затрати на проектування та відпрацювати зворотній зв’язок даних з процесом.

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

У загальному процесі проектування виділяють мікро- та макро-процеси. Мікро-процес має спіральну модель розвитку та є каркасом для ітеративного підходу до розвитку програмного продукту. Макро-процес задає рамки які обмежують та направляють мікро-процес.

Мікро-процес об’єктно-орієнтованого проектування

Мікро-процес проектування регламентує повсякденну працю як окремого програміста так і невеликої групи розробників.

Мікро-процес проектування об’єктно-орієнтованого програмного забезпечення складається з наступних видів діяльності:

- виявлення класів та об’єктів на данім рівні абстракції;

- уточнення семантики цих класів та об’єктів;

- уточнення зв’язків між цими класами та об’єктами;

- специфікація інтерфейсу та реалізація цих класів та об’єктів.

Виявлення класів та об’єктів.

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

Результати. Створення словника предметної області.

Види діяльності.

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

- застосовуючи техніку аналізу поведінки виявити основні класи та об’єкти, що будуть функціональними точками системи;

- для відповідних сценаріїв, що створені в певному макро-процесі проаналізувати з поступовим переходом до деталізації поведінки системи.

Уточнення семантики.

Мета. Визначити поведінку та атрибути кожної абстракції виявленої на попередньому кроці.

Результати. Уточнення словника даних, описання інтерфейсів класів, створення діаграм об’єктів та їх взаємодії.

Види діяльності.

З даним кроком пов’язані три види діяльності:

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

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

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

Уточнення зв’язків.

Мета. Уточнити межі кожної з абстракцій, розпізнати всі сутності з якими вони взаємодіють.

Результати. Діаграми класів, об’єктів та модулів.

Види діяльності.

На цьому кроці виділяють три види діяльності:

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

- ідентифікація різних взаємодій (провірити кожний механізм взаємодії в центральних та периферійних сценаріях, виявити паралелізм, визначити об’єкти – актори, агенти та сервери і способи синхронізації між ними; переглянути та оптимізувати ієрархії класів; згрупувати класи в модулі та підсистеми; розподілити класи та об’єкти в модулі);

- уточнення асоціацій ().

Реалізація класів та об’єктів.

Мета. Довести створені абстракції до рівня, який достатній для виявлення нових класів та об’єктів на наступному рівні абстракцій (переході на наступну ітерацію).

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

Види діяльності.

- перегляд протоколів кожного з класів, ідентифікувати стереотипи їх поведінки;

- виявити можливість використання пераметризованих класів, закритого або захищеного наслідування;

- переглянути зобов’язання об’єктів їх семантику та поведінку з метою забезпечення ефективності реалізації на вибраній мові програмування;

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

Макро-процес об’єктно-орієнтованого проектування

(Продовжити)

Частина ІІ

Об’єктно-орієнтоване програмування під ОС Windows

Тема № 5: Основи операційної системи Windows

План

1. Основи операційних систем та класифікація ОС Windows.

2. Інтерфейс прикладного програмування (Application programming interface - API).

3. Графічний інтерфейс користувача (User graphic interface – GUI).

Основи операційних систем.

Операційна система (ОС) Windows на даний час є стандартом де-факто для персональних комп’ютерів. У світі спостерігається найбільша кількість продаж та найширше розповсюдження саме цієї ОС.

Довільна сучасна операційна система повинна володіти набором можливостей для керування найбільш важливими ресурсами комп’ютера, серед яких:

Пам’ять. ОС керує великим, неструктурованим простором адрес і при необхідності неявно переміщує дані з фізичної пам’яті на диск та у зворотному напрямку.

Файлова система. ОС керує простором іменованих файлів та забезпечує як прямий, так і послідовний доступ до даних, а також керує каталогами (папками) та файлами. Більша частина системи має ієрархічний простір імен.

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

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

Зв’язок та синхронізація. ОС керує зв’язком та синхронізацією між задачами в межах однієї системи, а також з’єднанням між системами через мережу, локальну чи глобальну.

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

Усім згаданим вимогам в певній мірі відповідає ОС Windows, а саме Win32. Хоча в деяких випадках існують обмеження на повноту ОС в залежності від платформи. На даний час підтримуються наступні платформи під загальною назвою Windows.

- Windows XP, об’єднує в собі можливості гілки Windows 2000 (NT) та Windows 9х. З одного боку захищеність, а іншого можливість прямого доступу до ресурсів.

- Windows 2000, продовження гілки NT – захищеної ОС. Призначена для серверів та потужних настільних систем.

- Windows NT 4.0, захищена ОС. В даний час за NT вважають NT 4 Service Pack 3 (SP 3), найбільш повну позбавлену первинних недоліків ОС.

- Windows 95 та Windows 98 (Windows 9х) – настільні ОС, що не мають системи безпеки. Основне їх призначення – плавний перехід від DOS до Windows NT .

- Windows CE, призначена для малих систем та вбудованих процесорів и забезпечує дяку підмножину функцій Win32.

ОС Windows зараз домінує на ринку ОС для персональних та малих комп’ютерів. У той же час поступово просувається і на ринок великих машин. Основою успіху цієї ОС є вдала маркетингова політика та орієнтація на одночасно на користувача та розробника прикладного програмного забезпечення. Незважаючи на справедливу критику стосовно надійності та завершеності ОС завойовування нею ринку буде продовжуватися.

До основних переваг ОС Windows можна віднести:

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

- зручний контекстно зрозумілий інтерфейс користувача;

- можливість роботи на широкому спектрі апаратних платформ;

- Windows NT та Windows 2000 сертифіковані в Агентстві національної безпеки США за рівнем С2;

- в ОС Windows інтегровані деякі можливості, що в сукупності недоступні іншим операційним системам (потоки, рівень безпеки С2).

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

Інтерфейс прикладного програмування (API)

Основним посередником між ядром операційної системи та прикладним програмним забезпеченням є Інтерфейс прикладного програмування (Application programming interface - API). Саме через функції API розробник програмного забезпечення спілкується з операційною системою. Гнучкість, наповненість та ефективність функцій API визначає успіх операційної системи. Сучасні версії Windows базуються на API Win32, 32-розрідному API, до якого входить більше ніж 2000 функцій та більше чим 200 макровизначень, повідомлень та констант.

Для успішного освоєння Win32 необхідно запам’ятати декілька основних особливостей, які впливають на стиль програмування та методику керування, серед них:

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

- об’єктом ядра повинен керувати API Win32 і нема ніяких “обхідних шляхів”. Ця умова відповідає принципам абстракції даних в об’єктно-орієнтованому програмуванні;

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

- API Win32 містить багато функцій, що виконують однакові або подібні дії; зокрему функції “напівфабрикати”, що об’єднують в одній функції послідовності функцій, що найчастіше викликаються;

- у Win32 входять різноманітні механізми синхронізації та взаємодії, що спеціалізовані для різних вимог;

- основним модулем виконання в Win32 є потік. Один процес може мати оин або декілька потоків;

- функції Win32 мають довгі змістовно навантажені імена;

- імена стандартних типів для API складаються з символів верхнього регістру та також мають змістовне навантаження;

- для вказівників на стандартні типи використовується макровизначення в яких застосовується префікс LP.

На даний час ОС Windows базується на API Win32, але інтенсивно ведуться роботи над платформою Win64. Перехід на цю платформу зумовлений прагненням розширити спектр застосування Windows на великих машинах та апаратно програмних комплексах.

Графічний інтерфейс користувача (GUI)

Основним посередником між ОС Windows та користувачем є Графічний інтерфейс користувача (User graphic interface – GUI). Розробники ОС прагнули зробити його не тільки зручним та інтуїтивно зрозумілим для роботи, але й щоб програми від інших розробників за своїм інтерфейсом на значно відрізнялися від системних програм. Дотримання однакового інтерфейсу, однакових механізмів роботи з програмами (засобів керування), дозволяє користувачу швидше освоювати ці продукти та не створює дискомфорту при роботі з багатьма програмними продуктами різного призначення.

Основні елементи GUI.

До основних елементів GUI ОС Windows можна віднести наступні:

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

діалогове вікно (Dialog Box) – тимчасове вікно для запиту в користувача введення даних, зазвичай додаткових даних. Розрізняють модальні та немодальні блоки діалогу. Модальне вікно вимагає обов’язкової відповіді користувача і блокує всі інші вікна програми;

елемент керування (Control) – дочірнє вікно, що розміщене на основному вікні та служить для введення/виведення елементарних даних.

До основних простих елементів керування відносяться:

кнопка (Button) – зазвичай повідомляє програму, що користувач вибрав (натиснув) цей елемент. Розрізняють декілька видів кнопок: звичайна, що спрацьовує при натисканні (Push Button); перемикач (Radio Button); прапорець (Check Button) та група (Group Box);

текстове поле (Edit) – простий редактор, що дозволяє проглядати, вводити та редагувати текст;

список (List Box) – засіб для відображення списку елементів з можливістю вибору одного або декількох з них;

комбінований список (Combo Box) – комбінація списку з текстовим полем Дозволяє вибрати та редагувати елемент списку;

смуга прокрутки (Scroll Bar) – дозволяє вибрати напрямок та прокручувати інформацію у зв’язаному з нею вікні;

статичний елемент (Static) – застосовується як коментар для інших елементів керування, не піддається дії зі сторони користувача.

Крім простих елементів керування ОС Windows підтримує багато загальних елементів керування (common controls), які володіють розширеною функціональністю, досить часто використовуються при створенні графічного інтерфейсу користувача та стали стандартом де-факто для багатьох віконних програм. До них можна віднести:

змінюваний список (Drag List Box) – тип списку, що дозволяє перетягувати його елементи міняючи порядок їх відображення.

перегляд списку (List View) – засіб для структурованого перегляду списку, допускає відображення піктограм та тексту. Дозволяє побудувати список у вигляді таблиці.

рядок стану (Status Line) - горизонтальна смуга в нижній частині батьківського вікна призначена для відображення різноманітної інформації про біжучий стан програми.

панель інструментів (Tool Bar) – дочірнє вікно, що складається з набору кнопок для швидкого виклику команд.

підказка (Tool Tip) – спливаюче вікно з текстом, що коротко пояснює елемент керування.

перегляд дерева (Tree View) – вікно для перегляду даних, що організовані у вигляді дерева. Кожен елемент дерева може містити певну піктограму.

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

вікно властивостей (Property Sheet) – вікно, що дозволяє переглядати та редагувати властивості деякого об’єкту. Воно будується на основі “вкладок”. Дозволяє будувати діалоги з послідовним гортанням сторінок.

гаряча клавіша (Hot Key) – дозволяє задавати комбінації клавіш для швидкого виклику команди. Цей засіб відображає вибір користувача та перевіряє чи він є допустимим.

індикатор (Progress Bar) – засіб для індикації тривалої роботи процесу або операції. Відображається у вигляді прямокутника, що поступово заповнюється.

лінійка з повзунком (Track Bar) – елемент керування у вигляді повзунка та лінійки з насічками, який дозволяє плавно міняти числове значення зв’язане з ним від мінімуму до максимуму.

лічильник (Up-Down) – пара кнопок зі стрілками для дискретного збільшення або зменшення зв’язаного з цим лічильником елемента керування.

розширений редактор (Rich Edit) – редактор RTF-формату з розширеними можливостями. Дозволяє керувати шрифтами, кольором, розміром, вирівнюванням. Підтримує роботу з OLE об’єктами.

перегляд відео-кліпів (Animator) – дозволяє переглядати відео-кліпи у форматі AVI (Audio Video Interleaved). Відображає тільки відео без звукового оформлення.

На основі базових елементів керування будуються всі інші. Зокрема зі стандартною бібліотекою поставляються вже готові діалоги. Наприклад: для навігації файловою системою; вибору параметрів шрифту; вибору кольору та інші.

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

Ресурси програм.

Кожна програма володіє низкою стандартних ресурсів або ресурсів користувача. Ресурсами програми називаються масиви даних які розробник може добавити у модуль, що виконується. До стандартних ресурсів відносяться:

- Акселератори (accelerators) – структури даних, що містять списки гарячих клавіш.

- Бітові масиви (bitmaps) – піктограми, що відображаються на елементах керування.

- Курсори (cursors) – бітові масиви, що використовуються як вказівники миші.

- Значки (icons) – бітові масиви, що використовуються для візуалізації різноманітних об’єктів у системі та програмі.

- Шаблони діалогів (dialogs) – заготовки типових діалогових вікон з влаштованими засобами керування.

- Шаблони меню (menus) – заготовки для створення меню програми.

- Шаблони панелей інструментів (toolbars) – заготовки для створення панелей інструментів користувача.

- Таблиці символьних рядків (string tables) – списки статичних символьних масивів (макровизначень), що використовуються програмою.

Ресурси зберігаються в структурованому вигляді у файлі та компілюються окремо. Вони можуть використовуватися різними програмами.

Доступ до ресурсів відбувається за числовими або рядковими ідентифікаторами.

Керування графічним виводом.

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

Графічні об’єкти. Для відображення інформації в Windows використовуються наступні графічні об’єкти:

- бітові масиви (bitmaps) – прямокутні растрові зображення;

- олівці (pens) – задають параметри малювання ліній (товщина, колір та стиль);

- пензель (brushes) – задають параметри заливання замкнутих контурів (колір та стиль);

- шрифти (fonts) – задають параметри виводу тексту (ім’я шрифту, розмір та ін.);

- регіони (regions) – ділянки вікна правильної форми (прямокутник, еліпс) для виконання операцій в їх межах;

- логічні палітри (logical pallets) – забезпечують інтерфейс між програмою та кольоровим пристроєм виводу;

- контури (paths) – використовуються для означення контурів складних фігур.

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

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

У Win32 API визначено чотири типи контекстів пристроїв:

- екрану;

- друкарки;

- об’єктів в пам’яті (без безпосереднього виводу на екран);

- інформаційний (для отримання даних про пристрій).

З контекстом пристрою програма може виконувати наступні операції:

- перерахунок графічних об’єктів;

- встановлення нових графічних об’єктів;

- знищення графічних об’єктів;

- збереження та відновлення графічних об’єктів, їх атрибутів та графічних режимів.

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

Графічні режими. Графічні режими використовуються для встановлення способів змішування кольорів, місця, параметрів виводу та ін. ОС підтримуються п’ять графічних режимів:

- налаштування фону – визначає спосіб змішування кольорів фону текстових об’єктів та растрових зображень, фону поля виводу (вікна чи екрану);

- відображення (промальовування) – визначає спосіб змішування кольорів олівців, пензлів, текстових об’єктів та растрових об’єктів з фоном поля виводу.

- масштабування – визначає спосіб перетворення логічних координат при виводі у вікна, на екран або друкарку;

- заливання контурів – визначає спосіб використання пензлів при заливанні складних контурів;

- стиску – визначає спосіб перетворення кольорів растрових зображень при їх збільшуванні (зменшуванні).

Шрифти. ОС Windows підтримує роботу зі шрифтами наступних типів:

- растрові (кожен символ зберігається як бітовий масив);

- векторні (для символу зберігається ламана лінія, що описує його контури);

- TrueType (містять інформацію про контурну лінію символу та способи масштабування).

Растрові шрифти швидко відображаються але погано мастабуються. Векторні, навпаки, добре мастабуються але повільно відображаються.

Тема № 6 : Структура програм під ОС Windows

План

1. Основна функція програми під ОС Windows.

2. Стандартний цикл обробки повідомлень.

3. Віконна процедура.

4. Основні типи віконних повідомлень.

Основна функція програми під ОС Windows

Довільна програма під ОС Windows, що має інтерфейс з користувачем, складається з двох основних частин:

- функції WinMain (аналог функції main, що застосовується для консольних програм);

- функції вікна (залежить від типу інтерфейсу SDI, MD, Dialog).

Розглянемо задачі які повинна вирішувати функція WinMain програми, що підтримує одно-документний (SDI) чи багато-документний (MDI) інтерфейс. Наприклад, скорочена стандартна реалізації функції WinMain має вигляд:

int WINAPI VinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

MSG msg;

// Ініціалізація програми – підготовка даних класу вікна і його реєстрація

if (!InitApplication(hInstance))

return (FALSE);

// Завершення створення копії програми – створення основного вікна

if (!InitInstance(hInstance, nCmdShow))

return (FALSE);

// Цикл обробки повідомлень

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

// Завершення роботи програми – збереження налаштувань

ExitInstance(hInstance);

return (msg.wParam);

}

Функція складається з чотирьох частин:

- реєстрація класу вікна – функція InitApplication;

- створення основного вікна програми - функція InitInstance;

- цикл обробки повідомлень – цикл while;

- збереження налаштувань програми - функція ExitInstance.

Функції реєстрації програми InitApplication та створення основного вікна InitInstance не реалізовані в системі, тому вини повинні бути повністю реалізовані розробником. В бібліотеці MFC реалізовано відповідний клас CwinApp, що відповідає за згадані задачі. Відповідні методи реалізовані в ньому як абстрактні, тому також повинні реалізовуватися розробником.

Нижче наведено типову реалізацію цих функцій:

BOOL InitApplication(HINSTANCE hInstance)

{

WNDCLASS wc;

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WndProc;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

wc.lpszMenuName = NULL;

wc.lpszClassName = szAppName;

return RegisterClass(&wc);

}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

HWND hWnd;

HWnd = CreateWindow( szAppName, szTitle, WS_OVERLAPPEDWINDOW,

CW_DEFAULT, 0, CW_USERDEFAULT, 0,

NULL, NULL, hInstance, NULL)

if (!hWnd)

return (FALSE);

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

return (TRUE);

}

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

Стандартний цикл обробки повідомлень

Windows – це ОС, що базується на повідомленнях. Підсистема обміну повідомленнями є ядром ОС, тому знання механізму обробки повідомлень допоможе краще зрозуміти роботу систему та ефективно керувати прикладними програмами. Підхід до побудови програм на основі повідомлень вперше був застосований в ОС для комп’ютерів Macintosh. Він ґрунтується на тому, що поведінка (реакція) програми визначається зовнішніми подіями (повідомлення). Цей факт добре перекликається з концепцією об’єктно-орієнтованого програмування. Тому програмування для Windows за своєю суттю є об’єктно-орієнтованим, навіть якщо використовуються не об’єктно-орієнтовані мови.

Windows – програма складається з множини об’єктів, що обмінюються між собою повідомленнями. Основним об’єктом на якому базується механізм повідомлень є вікно. Воно першим появляється при запуску програми та останнім зникає при її завершенні. В процесі роботи програми ОС повідомляє прикладній програмі, точніше її вікну, про якусь подію за допомогою повідомлення, яке описує зміни, що відбулися в середовищі яке оточує програму. Механізм повідомлень, також єдиний спосіб “спілкування програми зі своїми вікнами. Обмін повідомленнями відбувається через ОС, яка отримавши повідомлення доставляє його відповідному об’єкту (вікну).

Кожне повідомлення зв’язується з певним вікном. З кожним повідомленням у вікні зв’язана програма, що реагує на нього – обробник повідомлень. Кожне повідомлення ОС розміщає в первинну чергу повідомлень з якої повідомлення розподіляються між об’єктами. В Win32 (на відміну від Win16, де існує тільки черга) може створюватися черга повідомлень для кожного процесу а також потоку, що позитивно позначається на швидкості та надійності виконання програм. В межах вікна існують механізми обміну повідомленнями, які дозволяють обійти загальну чергу повідомлень, що розвантажую загальну чергу та пришвидшує виконання прикладних програм.

До загальних функцій розсилання повідомлень відносяться:

SengMessage() – посилає повідомлення об’єктові на пряму та очікує його опрацювання (синхронний обмін);

PostMessage() – посилає повідомлення в чергу та завершується (синхронний обмін).

Варто зауважити, що функція SengMessage() дещо обходить механізм обміну повідомленнями ОС Windows.

Стандартний цикл обробки повідомлень в основній функції програми має вигляд:

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

Цей цикл працює протягом всього часу роботи програми. Кожна ітерація – це видобування одного повідомлення з черги повідомлень біжучого потоку, за що відповідає функція GetMessage(), другий аргумент котрої (NULL) вказує, що обробляються повідомлення для всіх вікон. Третій та четвертий параметри задають фільтр повідомлень (min, max). Вибране повідомлення помішається в спеціальну структуру MSG:

typedef struct tagNSG {

HWND hWnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

} MSG;

де hWnd – (дескриптор) логічний номер вікна, якому адресується повідомлення; message – ідентифікатор (номер) повідомлення; wParam і lParam – додаткові дані повідомлення, їх наповнення залежать від типу повідомлення; time - час посилання повідомлення; pt - положення курсору миші в час посилання повідомлення.

Функція TranslateMessage() призначена для перетворення віртуальних кодів клавіатури в ASCII – коди, що дозволяє розрізняти великі та малі літери без додаткового аналізу стану клавіш регістру.

Функція DispatchMessage() призначена для передачі повідомлення у відповідну віконну процедуру для обробки.

Функція GetMessage() завжди повертає істинне значення окрім одного випадку. Прийнявши повідомлення WM_QUIT вона завершується з кодом FALSE і цикл завершується.

Отже цикл обробки повідомлень є основою будь-якої програми Windows.

Віконна процедура

Основним отримувачем повідомлень є спеціальна віконна процедура, якій йдуть з черги повідомлень програми (точніше – з потоку програми). У цій процедурі повідомлення обробляються індивідуально:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch (message) {

case WM_DESTROY :

PostQuitMessage(0);

break;

default :

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

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

Функція PostQuitMessage() – це найпростіший обробник повідомлень, що застосовуються при написанні програм. Обробник повідомлень – це функція, що відповідає тільки за обробку одного-єдиного повідомлення.

Основні типи віконних повідомлень

На даний час ОС Windows налічує більше ніж 900 стандартних повідомлень, але більшість з них використовуються досить рідко. Наприклад, одні характерні тільки для певного типу вікна, інші відіграють особливу роль при використанні певного типу інтерфейсу (SDI чи MDI). Треті проходять через цикл повідомлень, але ніколи не використовуються (внутрішні повідомлення Windows створені для її власної мети).

Всі імена повідомлень, що використовується при створення інтерфейсу користувача, визначені в файлі заголовків WINUSER.H наступним чином:

#define WM_COMMAND 0x0111

або

#define WM_PAINT 0x000F

Префікс WM_ вказує, що це “віконне повідомлення” (Windows Message). Ці символічні імена визначають числа, що служать ідентифікаторами повідомлень. Інші префікси вказують на відношення повідомлення до певного виду вікна (елементу керування), наприклад: EM_ (Edit Message) – елементу керування EDIT; BM_ (Button Message) – елементу керування BUTTON; та інше.

Окрім стандартних повідомлень в ОС передбачена можливість створення власних повідомлень розробником прикладного програмного забезпечення.

Тема № 7 : Бібліотека базових класів Microsoft (MFC)

План

1. Огляд бібліотеки базових класів Microsoft.

2. Ієрархія класів MFC.

3. Основні архітектури програм на основі MFC.

4. Макровизначення, глобальні функції та змінні.

Огляд бібліотеки базових класів Microsoft

Розробка програмного забезпечення з нуля є доволі складною та тривалою в часі задачею. Тому, більшість програм створюється шляхом розвитку якоїсь базової програми, типової заготовки або шаблону. Для створення професійного програмного забезпечення необхідно чітко уявляти внутрішню структуру та логіку роботи засобів, що використовуються. Особливо це відноситься до засобів створення програмних продуктів. Розглянемо бібліотеку базових класів Microsoft (Microsoft Foundation Classes, MFS) саме як інструмент, що повинен взяти на себе більшу частину чорнової роботи з створення програмного забезпечення. Тільки знання всіх нюансів побудови, функціонування та можливостей бібліотеки дозволить легко створювати програми довільної складності.

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

- значне зменшення зусиль при створенні програмних продуктів;

- швидкість виконання програм, написаних з використанням бібліотеки, повинна відповідати швидкості виконання програм, що написані на мові С з використанням Win32 API;

- розмір допоміжного коду повинен бути мінімальним;

- здатність прямо викликати довільну С-функцію Win32 API;

- легкість використання Win32 API в С++ повинна бути такою ж як при використанні традиційної мови С.

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

За короткий відрізок часу корпорацією Microsoft було розроблено декілька версій бібліотеки MFC, яка ставала все далі потужнішою та зручнішою. Версія 4.3 бібліотеки MFC не є останньою і швидше за все невдовзі на ринку появиться наступна версія. До того ж підтримка іншими компаніями та їх компіляторами і засобами розробки програмного забезпечення саме бібліотеки MFC, а не OWL фірми Borland International чи власних продуктів дозволяє концентрувати увагу саме на MFC. Навіть фірма Borland International, що є конкурентом Microsoft в створенні компіляторів для персональних комп’ютерів придбала ліцензію на використання в своїх продуктах MFC.

Ієрархія класів MFC

На рис. __ показана ієрархія основних категорій класів бібліотеки MFC. Кожен з похідних класів володіє властивостями батьківських класів та набуває нових характерних тільки йому. В основі ієрархії лежить єдиний базовий клас CObject. Всі решту класи за відношенням до нього діляться на дві категорії: похідні та не похідні від нього. Отже, архітектуру MFC можна охарактеризувати як ліс класів.

CObject. Клас, що коренем більшої частини класів бібліотеки MFC. Основним призначенням цього класу є надання похідним від нього класам можливостей:

- зберігання інформації про клас часу виконання;

- підтримка серіалізації та діагностики об’єкту.

Єдиною змінною-членом цього класу є змінна classObject типу CRuntimeClass, що зберігає інформацію про об’єкт часу виконання яка асоціюється з класом CObject.

Під терміном “серіалізація” (serialize) розуміють процес зберігання в послідовній формі біжучого стану об’єкту на якомусь пристрої постійного зберігання (найчастіше в файлі на диску) з можливістю наступного відновлення цього стану. Тобто сам об’єкт відповідає за спосіб та формат запису читання свого стану.

Рис. __. Ієрархія класів за категоріями.

Клас CObject не підтримує множинного наслідування. Тобто, класи, що створюються можуть містити тільки один об’єкт CObject і він повинен бути “самим лівим” в ієрархії.

class A : public CObject { … };

class B : { … };

class C : { … };

class D : public A, B, C { … };

Кожен похідний від CObject клас асоціюється зі структурою типу CRuntimeClass, яка використовується для отримання інформації часу виконання. Для використання цієї структури та механізму динамічної діагностики необхідно включити в описанні класу одне з макровизначень DECLARE_DYNAMIC, DECLARE_DYNCREATE або DECLARE_SERIAL, та в відповідне макровизначення IMPLEMENT_DYNAMIC, IMPLEMENT_DYNCREATE або IMPLEMENT_SERIAL в реалізації класу.

Інформацію про клас можна отримати за допомогою функції члена IsKindOf(). Вона дозволяє перевірити, чи є об’єкт певного класу. Її параметром є об’єкт класу CRuntimeClass, який можна отримати за допомогою макро-функції RUNTIME_CLASS з іменем класу.

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

Основною архітектурою прийнятою в MFC, що володіє повним спектром функціональних можливостей є архітектура “Документ/Вигляд”. Усі решта архітектури, на основі діалогового вікна, форми чи DLL, використовують лиш деяку частину можливостей згаданої архітектури.

Класи програм та потоків. Бібліотека MFC підтримує багато потоків, але один з них є основним, він використовує об’єкт класу CWinApp. Прикладна програма будується шляхом наслідування цього класу. Клас CwinThread інкапсулює частину можливостей ОС для роботи з потоками. Окрім того, MFC містить класи об’єктів синхронізації, надаючи інтерфейс С++ об’єктам синхронізації Win32.

. . .

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

SDI – одно-документна програма.

MDI – багато-документна програма.

Діалог – програма на основі діалогу.

Форма – програма на основі форми.

. . .

Модель об’єкту часу виконання

Для забезпечення обслуговування об’єктів в період їх виконання та підтримки властивостей, що надаються класами CObject та CRuntimeClass використовуються наступні макровизначення:

DECLARE_DYNAMIC(className) - дозволяє доступ до інформації часу виконання про об’єкт класу (повинен використовуватися при оголошенні класу); className - дійсне ім’я С++ класу без лапок.

IMPLEMENT_DYNAMIC(className, baseClassName) – генерує код, необхідний для динамічного отримання інформації часу виконання про об’єкт (повинен використовуватися в реалізації класу).

DECLARE_DYNCREATE(className) – дозволяє динамічне створення об’єкту та отримання інформації часу виконання про об’єкт класу (повинен використовуватися при оголошенні класу).

IMPLEMENT_DYNCREATE(className, baseClassName) – дозволяє динамічне створення об’єкту та доступ до інформації часу виконання про об’єкт класу (повинен використовуватися в реалізації класу).

DECLARE_DYNAMIC(className) – для класів описаних за допомогою приведених вище макросів повертає вказівник на структуру CRuntimeClass, яка відповідає класу className.

Діагностика об’єкту

Діагностичний сервіс, що надається бібліотекою MFC значно спрощує відлагодження програм. Він включає макровизначення та глобальні функції, які дозволяють відслідковувати розподіл пам’яті, вміст дампу об’єкту та друк відлагоджувальної інформації часу виконання.

До основних макросів діагностики відносяться:

ASSERT(booleanExpression) – перериває виконання програми, якщо вираз booleanExpression, що обчислюється рівний FALSE, та друкує повідомлення про помилку.

ASSERT_KINDOF(className, pObject) – перевіряє, чи є pObject об’єктом класу className, де className - ім’я класу похідного від CObject;

ASSERT_VALID(pObject) – використовується для перевірки внутрішнього стану класу pObject на доступність, якщо хоч одна перевірка приводить до помилки, то макрос виводить повідомлення аналогічно ASSERT.

TRACE(exp) – дозволяє вивести на екран фор матований рядок, що визначається виразом exp подібно до функції printf().

TRACE0, …, TRACE3 – спрощені версії макросу TRACE для виводу форматованих рядків з кількістю аргументів від 0 до3.

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

VERIFY(booleanExpression) – працює аналогічно до ASSERT, але в робочій версії бібліотеки

Глобальні змінні.

CdumpContext afxDump – визначена змінна, що дозволяє послати інформацію у вікно відлагоджувача.

BOOL afxTraceEnable – використовується для дозволу або заборони роботи макросів TRACE.

Тема № 8: Структура програми на основі класів MFC

План

1. Файли, що включаються в проект. Домовленість, щодо імен в MFC.

2. Функція WinMain() та клас CWinApp.

3. Обробка повідомлень в MFC.

Файли, що включаються в проект

Середовище Visual C++ містить діалоговий інструмент, так званий “майстер”, для інтерактивного створення проекту. Цей інструмент дозволяє створити заготовки проектів усіх основних типів програм, що використовуються в Windows. При створенні проекту генерується заготовка основної програми та деякі допоміжні файли, що необхідні для компіляції проекту вибраного типу. Розглянемо основні файли, що автоматично включаються в проект віконної програми з використанням MFC.

Основним файлом, що включається у віконну програму є ‘StdAfx.h’. Він служить для включення в програму описання всіх основних бібліотечних функції, що необхідні для даного типу програми. Включення цього файлу в проект дозволяє ефективно використовувати механізм попередньої компіляції, що значно економить час на повторну компіляцію проекту.

В свою чергу файл ‘StdAfx.h’ включає заголовкові файли:

AFX.H – включає класи загального призначення, базові типи MFC і заголовки базових функцій C;

AFXRES.H – включає стандартні ідентифікатори ресурсів;

WINDOWS.H – включає описання функцій, структур та макровизначень Win32 API та для зручності розбитий на чотири частини WINDDEF.H, WINBASE.H, WINGDI.H та WINUSER.H;

WINDDEF.H – містить визначення базових типів;

WINBASE.H – містить описання функцій Win32 API;

WINGDI.H та WINUSER.H – містить оголошення функцій модулів GDI та USER і відповідні їм типи, структури та макровизначення.

Домовленість, що імен в MFC

Для іменування класів у MFC прийнято вживати префікс ‘C’ (наприклад CwinApp, CDialog, CWnd).

Іменування функцій (методів) є контекстно зрозумілим. Для використовуються три способи:

- об’єднують дієслово та іменник (наприклад, LoadIcan – завантажити іконку, DrawText – намалювати текст);

- структурована назва складається тільки з іменника (наприклад, DialogBox – блок діалогу);

- вказується напрямок перетворення даних (наприклад, XtoY – з X в Y).

Для змінних членів класу MFC використовується префікс ‘m_’ (від class members) за яким іде префікс, що характеризує тип даних на завершення структуроване ім’я змінної (наприклад, ‘m_pMainWnd’, де p – вказує на тип вказівник).

Функція WinMain() та клас CWinApp

При створенні проекту за допомогою “майстра” проектів, функція WinMain() явно не створюється. Вона захована в типовий об’єктний код заготовки програми. Такий підхід звільняє розробника від написання багатьох рядків рутинного додаткового коду для виконання стандартних кроків ініціалізації програми. Основні моменти в реалізації цієї функції розглядалися в попередніх лекціях. Тут слід зауважити, що у файлі реалізації основного класу програми CWinApp, створюється єдиний глобальний об’єкт цього класу з фіксованим іменем theApp, який і служить точкою входу в програму, а оболонка використовує це наперед визначене ім’я для запуску програми. В бібліотеці реалізовано функція, яка повертає вказівник на цей глобальний об’єкт.

CWinApp* pApp = AfxGetApp();

Клас CWinApp

Клас CWinApp є базовим класом від якого утворюється обов’язковий об’єкт-програма для Windows на основі MFC, його ієрархія показана на рис. __.

Рис. __. Ієрархія класу CWinApp

Основними задачами цього класу є:

- ініціалізація основного вікна програми;

- створення та опитування черги повідомлень;

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

Розглянемо основні змінні- та функції-члени класу CWinApp.

Змінні-члени класу:

LPSTR m_pszAppName – назва програми (загальнодоступна).

HINSTANCE m_hInstance – копія логічного номеру (дескриптора) програми (загальнодоступна).

LPSTR m_lpCmdLine – копія вказівника на командний рядок програми (загальнодоступна).

int m_nCmdShow – копія параметру, що визначає первинне відображення вікна програми на екрані (загальнодоступна).

LPSTR m_pszExeName – назва завантажувального модуля програми без розширення (загальнодоступна).

LPSTR m_pszProfileName – назва INI-файлу програми (загальнодоступна).

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

Додатково клас CWinApp на слідує від класу CWinThread змінну m_pMainWnd типу CWnd, яка зберігає вказівник на об’єкт основного вікна біжучого потоку. Windows автоматично завершує програму, якщо закривається основне вікно.

Функції ініціалізації програми:

void LoadStdProfileSetting(UINT nMaxNRU = _AFX_MRU_COUNT) – завантажує список останніх файлів, що використовувалися (_AFX_MRU_COUNT=4).

void SetRegistryKey(LPCSTR lpszRegistryKey) – заставляє програму зберігати первинні на лаштування в реєстрі Windows а не в INI-файлі. Ключ lpszRegistryKey, за яким зберігаються ініціалізаційні дані програми в реєстрі має формат:

HKEY_CURENT_USER\Software\<НазваОрганізації>\<Програма>\<Секція>\<Змінна>

void EnableShellOpen() – дозволяє запускати програму подвійним клацанням миші на файлі, що асоціюється з програмою.

void RegistryShellFileTypes() – реєструє в реєстрі Windows типи файлів, що асоціюється з програмою.

void ParseCommandLine(CCommandLineInfo& rCmdInfo) – здійснює синтаксичний та семантичний аналіз командного рядка, що передається програмі та поміщає результати в об’єкт rCmdInfo.

BOOL ProcessShellCommand(CCommandLineInfo& rCmdInfo) – обробляє дані (аргументи) командного рядка.

UINT GetProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int Def) – при успішному пошуку - повертає ціле число, що міститься полі lpszEntry секції lpszSection реєстру чи INI-файлу, інакше значення за замовчуванням (Def).

BOOL WriteProfileInt(LPCTSTR lpszSection,LPCTSTR lpszEntry,int nVal) – записує ціле число (nVal) у відповідне поле секції реєстру чи INI-файлу. Якщо ключова запис відсутня то вона створюється.

CString GetProfileString(…), BOOL WriteProfileString(…) – читання, запис символьного рядка.

BOOL GetProfileBinary(…), BOOL WriteProfileBinary(…) – читання, запис двійкового масиву даних.

Основні віртуальні функції керування програмою:

virtual BOOL InitInstance() – викликається кожен раз при ініціалізації нового (додаткового) екземпляру програми. Ії наповнення повністю визначається розробником (в базовому класі, вона порожня). У ній зчитуються ініціалізаційні дані, реєструється та створюється основне вікно програми та інше.

virtual BOOL ExitInstance() – викликається з методу Run() для завершення біжучого екземпляру програми. У ній реалізовується збереження налаштувань програми в реєстрі чи INI-файлі, звільняються виділені програмі ресурси (ресурси стандартних елементів створених MFC звільняється автоматично).

virtual BOOL Run() – запускає та обслуговує основний цикл повідомлень програми, доки не отримає повідомлення WM_QUIT. Якщо черга повідомлень порожня, то запускає віртуальну функцію OnIdle() для фонової обробки.

virtual BOOL OnIdle(LONG lCount) – викликається в циклі обробки повідомлень, коли черга повідомлень порожня. Вона обновлює команди користувацького інтерфейсу, перемальовує елементи керування та інше. При пере визначення цієї функції обов’язково необхідно викликати функцію CwinnApp::OnIdle() базового класу. lCount – лічильник вкладених викликів.

Реєстрація класу вікна та його створення

Реєстрація та створення основного вікна програми на основі класів MFC майже нічим не відрізняється від подібної процедури на основі Win32 API, що розглядалася раніше. Основні відмінності спостерігаються при використання архітектури “Документ/вигляд”, яка баде розглянута на наступній лекції.

Обробка повідомлень в MFC

Основний цикл обробки повідомлень реалізований в методі Run() класу CwinnApp. Він діє аналогічно до розглянутого раніше циклу обробки повідомлень Win32 API за винятком запуску методу OnIdle() для виконання фонових операцій коли черга повідомлень порожня.

Категорії повідомлень MFC. У бібліотеці MFC прийнята класифікація повідомлень відмінна від Win32 API. Усі повідомлення розділено на три категорії:

- повідомлення Windows;

- повідомлення елементів керування;

- командні повідомлення (команди).

Цей розподіл зумовлений, що повідомлення кожної з категорій обробляються по-різному. В першу категорію входять повідомлення імена яких починаються з префіксу WM_, за винятком WM_COMMAND. Ці повідомлення обробляються вікнами і мають відповідні параметри які визначають алгоритм обробки. Сюди входять і апаратні повідомлення.

Друга категорія повідомлення (ивещения – notification message) від елементів керування та інших дочірніх вікон, що направляються батьківським вікнам. Наприклад, елемент керування посилає повідомлення своєму батьківському вікну, коли необхідно обновити інформацію про елементи списку. Батьківське вікно заповнює відповідну структуру та відсилає її назад елементу керування.

Механізми передачі та обробки повідомлень перших двох категорій повідомлень в основному однакові. Вони призначені для обробки віконними процедурами.

Третя категорія охоплює всі повідомлення WM_COMMAND, що називаються командами (або командними повідомленнями), від інтерфейсу користувача до якого входять меню, кнопки панелей інструментів и акселератори. Обробка команд відрізняється від обробки інших повідомлень і може проводитися багатьма об’єктами (документами, шаблонами документів і самим об’єктом “програма”).

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

Карта повідомлень. Для того, щоб об’єкт міг обробляти повідомлення необхідно привести у відповідність повідомлення та обробник, що відповідає за його опрацювання. Для цього служить карта повідомлень. Карта повідомлень реалізує механізм пересилання повідомлень та команд Windows у вікна, документи та інші об’єкти реалізовані на базі MFC. Карти повідомлень перетворюють повідомлення у виклик відповідних функцій класів, що їх обробляють. Кожен клас, який може отримати повідомлення, повинен мати свою карту повідомлень. Вона повинна визначатися за межами будь-якого С-блоку.

Для визначення карти повідомлень в MFC використовують три макроси: BEGIN_MESSAGE_MAP, END_MESSAGE_MAP та DECLARE_MESSAGE_MAP.

Макрос DECLARE_MESSAGE_MAP повинен розміщуватися в кінці оголошення класу, що використовує карту повідомлень.

Структура карти повідомлень є набором макросів, що взяті в спеціальні “операторні дужки” (формуються автоматично при використання майстрів створення класу в середовищі Visual C++):

BEGIN_MESSAGE_MAP(CTheClass, CBaseClass)

//{{AFX_MSG_MAP(CTheClass)

ON_WM_CREATE()

ON_WM_DESTROY()

ON_COMMAND(ID_CHAR_BOLD, OnCharBold)

ON_UPDATE_COMMAND_UI(ID_CHAR_BOLD, OnUpdateCharBold)

//}}AFX_MSG_MAP

ON_NOTIFY(FM_GETFORMAT, ID_VIEW_FORMATBAR, OnGetCharFormat)

ON_NOTIFY(FM_SETFORMAT, ID_VIEW_FORMATBAR, OnSetCharFormat)

END_MESSAGE_MAP()

Параметрами макросу BEGIN_MESSAGE_MAP() є ім’я класу для якого описується карта повідомлень (CTheClass) та ім’я його батьківського класу (CBaseClass). Між викликами цих двох макросів розміщені компоненти карти повідомлень.

Компоненти карти повідомлень. Для стандартного повідомлення Windows визначений свій макрос у формі:

ON_WM_XXX // XXX – ім’я повідомлення, наприклад ON_WM_CREATE

Імена обробників повідомлень формуються на основі наступної угоди: ім’я завжди починається з префіксу On, за яким йде ім’я відповідного повідомлення Windows без префіксу WM_. Наприклад для повідомлення WM_CREATE в класі CWnd визначено обробник:

afx_msg void OnCreate();

Описання всіх стандартних обробників повідомлень Windows можна знайти в файлі AFXWIN.H.

Командні повідомлення від меню, акселераторів та кнопок панелей інструментів обробляються макросом

ON_COMMAND(id, memberFunc)

Параметрами цього макросу є: id – ідентифікатор команди та memberFunc – довільне ім’я (але бажано дотримуватися угод Windows при іменування обробників) функції обробника повідомлення.

Для команд оновлення елементів керування використовується той же механізм але інший макрос

ON_UPDATE_COMMAND_UI(id, memberFunc)

А обробники таких команд мають вигляд.

afx_msg void memberFunc(CCmdUI *pCmdUI);

В обох випадках обробник memberFunc буде викликано тільки тоді, коли у віконну процедуру поступить команда з ідентифікатором id у параметрі wParam.

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

ON_CONTROL(wNotifyCode, id, memberFunc)

Цей макрос використовує як параметри код повідомлення wNotifyCode від елементу керування id та довільний обробник memberFunc. Обробник викликається тільки тоді, якщо код повідомлення елемента (наприклад BN_CLICKED) співпаде із зареєстрованим в карті повідомлень значенням wNotifyCode, а значення параметра співпаде з id.

Для найбільш поширених повідомлень від елементів керування використовуються спеціальні макроси. Наприклад:

ON_BN_CLICKED(id, memberFunc)

ON_EN_FOCUS(id, memberFunc)

ON_LBN_BDLCLK(id, memberFunc)

та інші. Повний список макросів цієї групи є в файлі AFXMSG.H.

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

ON_COMMAND_RANGE(idFirst, idLast, memberFunc)

ON_CONTROL_RANGE(wNotifyCode, idFirst, idLast, memberFunc)

ON_NOTiFY_RANGE(wNotifyCode, idFirst, idLast, memberFunc)

ON_UPDATE_COMMAND_UI_RANGE(idFirst, idLast, memberFunc)

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

ON_MESSAGE(WM_NAMEMSG, OnNameMsg)

Параметрами якого є номер (WM_MESSAGE) та ім’я (OnNameMsg) обробника повідомлення. Для того щоб номер повідомлення був унікальним в межах системи (програми) застосовують наступне оголошення:

#define WM_NAMEMSG (WM_USER + numb)

де numb – довільне додатне число (комбінація повинна бути унікальною).

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

ON_REGISTERED_MESSAGE(nMessageVariables, memberFunc)

де nMessageVariables – зареєстроване в Windows повідомлення (для отримання унікального ідентифікатора необхідно викликати функцію RegisterWindowMessage()), а memberFunc його обробник.

Для команд та повідомлень користувача обробники мають наступний прототип:

afx_msg LRESULT memberFunc(WPARAM wParam, LPARAM lParam);

де через wParam та lParam у функцію передається додаткова до повідомлення інформація.

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

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

Порядок в якому адресати пересилають команди зашитий в бібліотеці класів MFC і містить наступні кроки:

1. Команду отримує фрейм MDI (об’єкт класу CMDIFrameWnd). Порядок отримувачів:

- активне вікно класу CMDIChildWnd;

- сам об’єкт класу CMDIFrameWnd;

- програма класу CWinApp.

2. Команду отримує фрейм документу (об’єкт класу CFrameWnd, CMDIChildWnd) Порядок отримувачів:

- активний вид (view);

- біжучий фрейм документу;

- програма класу CWinApp.

3. Команду отримує вид (view). Порядок отримувачів:

- біжучий вид;

- документу приєднаний до виду.

4. Команду отримує документ. Порядок отримувачів:

- біжучий документ;

- шаблон документу, приєднаний до нього.

5. Команду отримує блок діалогу. Порядок отримувачів:

- біжучий блок діалогу;

- вікно, що володіє цим блоком діалогу;

- програма класу CWinApp.

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

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

UINT ::RegisterWindowMessage(LPCTSTR lpString);

Повертає унікальний ідентифікатор (в діапазоні від 0xC000 до 0xFFF) нового повідомлення. Як параметр lpString вона використовує вказівник на символьний рядок, що визначає ім’я повідомлення. Зареєстроване повідомлення діє до кінця сеансу роботи з Windows.

Приклад реєстрації повідомлення користувача.

class CUserWnd : public CParentWnd {

//{{AFX_MSG_MAP(CUserWnd)

afx_msg LRESULT OnSample(WPARAM wParam, LPARAM lParam);

//}}AFX_MSG_MAP

DECLARE_MESSAGE_MAP()

}

UINT WM_SAMPLE = RegisterWindowMessage(“MESSAGE_SAMPLE”);

BEGIN_MESSAGE_MAP(CUserWnd, CParentWnd)

//{{AFX_MSG_MAP(CUserWnd)

ON_REGISTERED_MESSAGE(WM_SAMPLE, OnSample)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

Тема № 9: Основні типи програм на основі класів MFC

План

1. Архітектура “Документ/вигляд”.

2. Програми з одним документом (SDI).

3. Програми з багатьма документами (MDI).

4. Програми на основі діалогу.

Архітектура “Документ/вигляд”

До основних типів програм, що будуються під Windows на основі бібліотеки MFC, можна віднести:

- одно-документні програми (SDI);

- багато-документні програми (MDI);

- програми на основі діалогу.

Перші два типи програм будуються на основі архітектури „Документ/Вигляд”. В основі цієї архітектури лежать три глобальні поняття рамка, документ та вигляд. Розглянемо детальніше ці поняття.

Документ. Під „документом” фірма Microsoft розуміє ті дані, що обробляються програмою. Під даними можна розуміти, що завгодно; звичайний текст, зображення, структуровані дані та інше. Ці дані зберігаються в основному в постійній пам’яті комп’ютера і відображаються у рамці програми.

Вигляд. Це спеціальне вікно в якому відображається вміст документу та за допомогою якого відбувається взаємодія з користувачем.

Таким чином спосіб зберігання даних в постійній пам’яті не впливає на спосіб їх представлення користувачу.

Рамка. В архітектурі „Документ/Вигляд”, рамка виконує роль посередника між документом та його виглядом. В її завдання входить: створити відповідні, для кожного з типів документів, вікна виглядів; координувати взаємодію між документами різних типів та їх виглядами. Поняття рамка найчастіше асоціюється з основним вікном програми.

Рамка в загальному складається з основного вікна де розміщено загальні елементи керування (меню, піктограми інструментів, рядок статусу і т.п.) та клієнтської області в якій міститься дочірнє вікно вигляду (SDI- програма) або дочірні вікна (MDI- програма). На рис. __ показано взаємозв’язок між об’єктами рамки, документу та вигляду.

В архітектурі „Документ/Вигляд” за взаємодію користувача з рамкою відповідає операційна система. А керування відображенням документа повністю лягає на плечі розробника.

Для координації створення та взаємодії між трьома основними об’єктами архітектури в бібліотеці класів MFC використовується спеціальні класи шаблонів документів, які створюються та керуються класом „програма”. Таким чином архітектура „Документ/Вигляд” охоплює наступні основні класи:

- CWinApp – створення єдиного об’єкту програми.

- CFrameWnd – клас створення основного вікна програми, базовий для класів CMDIFrameWnd та CMDIChildWnd.

- CDocTemplate – базовий абстрактний клас для створення шаблонів документів: для однодокументних програм від нього створюється клас CSingleDocTemplate; для багатодокументних програм - клас CMultiDocTemplate.

- CDocument – клас для створення документу.

- CView – базовий клас, що разом зі своїми нащадками – CctrlView, CEditView, CListView та іншими, відповідає за відображення даних документу та взаємодіє з користувачем.

Рис. __. Взаємозв’язок між об’єктами рамки, документу та вигляду

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

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

- документу;

- вигляду;

- рамки.

Конструктор класу, фіксує та зв’язує між собою типи класів документу, вигляду та рамки.

CDocTemplate:: CDocTemplate(UINT nIDResouce, CRuntimeClass *pDocClass,
CRuntimeClass *pFrameClass, CRuntimeClass *pViewClass);

де nIDResouce – ідентифікатор ресурсів (меню, акселератори, рядок описання типу шаблону), що асоціюються з даним типом документу; pDocClass, pFrameClass, pViewClass – вказівними типу CRuntimeClass на документ, рамку та вигляд, відповідно.

У клас шаблону документу CDocTemplate реалізовано декілька чисто-віртуальних функцій (GetFirstDocPosition, GetNextDoc, OpenFileName та ін.), тому цей клас є абстрактним і, як наслідок, його не можна використовувати безпосередньо. Найчастіше використовуються його класи-нащадки CSingleDocTemplate та CMultiDocTemplate, допомогою яких створюються одно- та багато-документні програми, відповідно. При потребі створення програми, що суттєво відрізняється від архітектури „Документ/Вигляд” необхідно створити свій шаблон документу на основі CDocTemplate, в якому перевизначити всі чисто-віртуальні функції.

Реєстрація шаблону документу та зв’язування документу, рамки та вигляду відбувається в методі InitInstance класу CWinApp.

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

Клас CMultiDocTemplate. Визначає шаблон документу для багато-документного інтерфейсу. В MDI-програмах основна рамка використовується як робоче місце, в якому користувач може відкрити довільну кількість документів одного чи різних типів.

У класі визначено два загальнодоступних члени, що відповідають за спільне використання меню та таблиці акселераторів:

HMENU CMultiDocTemplate::m_hMenuShared;

HACCEL CMultiDocTemplate::m_hAccelTables;

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

CPtrList CMultiDocTemplate::m_docList;

Доступ до елементів цього списку виконується через методи GetFirstDocPosition та GetNextDoc.

Конструктор класу має такі ж самі параметри як і конструктор базового класу.

При реєстрації та зв’язування документу, рамки та вигляду в SDI-програмі застосовується метод AddDocTemplate класу CWinApp. Нижче наведено приклад уривку коду функції InitInstance в якому реєструється два різних шаблона для MDI-програми.

CmulteDocTemplate* pDocTemplate;

// створення об’єкту шаблону для роботи з текстом

pDocTemplate = new CMultiDocTemplate( IDR_NOTETYPE,

RUNTIME_CLASS(CNoteDoc),

RUNTIME_CLASS(CNoteFrame),

RUNTIME_CLASS(CNoteView));

// додавання шаблону до списку

AddDocTemplate(pDocTemplate);

// створення об’єкту шаблону для роботи з текстом

pDocTemplate = new CMultiDocTemplate( IDR_DRAWTYPE,

RUNTIME_CLASS(CDrawDoc),

RUNTIME_CLASS(CDrawFrame),

RUNTIME_CLASS(CDrawView));

// додавання шаблону до списку

AddDocTemplate(pDocTemplate);

Документ та його вигляд.

Основним достоїнством архітектури „Документ/Вигляд” є розділення даних та їх відображення. Довільні зміни даних здійснюються через спеціальний клас документа, який забезпечує:

- необхідний простір для збереження даних в пам’яті;

- читає та записує дані на диск;

- забезпечує інтерфейс для доступу та поновлення даних.

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

Типова схема створення документу представлено на рис. __.

рис. __.Схема дій при створенні документу

Документ. Базовим класом для створення документів розробника є CDocument. У нього закладено типові функціональні можливості, що можуть знадобитися при роботі з документами довільних типів, такі як відкриття/закриття файлу, читання/запис даних у файл, зв’язок з виглядом (виглядами). Бібліотека MFC працює з документом використовуючи наперед визначений інтерфейс.

Об’єкти класу CDocument отримують команди від елементів керування інтерфейсу користувача в першу чергу. Якщо вони деяких з них не обробляють, то передають далі на обробку до шаблону.

До основних методів цього класу, що найчастіше використовуються, можна віднести:

const CString& CDocument::GetPathName()

- повертає шлях до файлу документу.

virtual void CDocument::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE)

- встановлює новий шлях до файлу документу та при необхідності добавляє його до списку часто використовуваних документів програми.

BOOL CDocument::IsModifed()

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

void CDocument::SetModifedFlag(BOOL bModifed = TRUE)

- дозволяє позначити документ як змінений.

void CDocument::AddView(CView *pView)

- приєднує новий вигляд до документу.

void CDocument::RemoveView(CView *pView)

- від’єднує заданий вигляд від документу.

virtual POSITION CDocument::GetFirstViewPosition()

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

virtual CView* CDocument::GetNextView(POSITION &rPosition)

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

Наступні віртуальні функції викликаються з MFC в процесі роботи з документом. Їх назви розкривають їхнє призначення.

virtual void CDocument::OnNewDocument();

virtual void CDocument::OnOpenDocument();

virtual void CDocument::OnSaveDocument();

virtual void CDocument::OnCloseDocument();

Перевизначивши дані методи можна забезпечити гнучке та детальне керування документом.

Вигляд. Для відображення документів у MFC створена спеціальна група класів, що забезпечують створення та управління дочірніми вікнами в яких зображується вміст документу. Базовим класом для них, як показано на рис. __ є CView.

Рис. __. Ієрархія класів виглядів документу

На відміну від документу, що може мати декілька виглядів одночасно, вигляд асоціюється тільки з одним документом. Для одного документу можна створити декілька рамок з виглядами або розділити одну рамку між декількома виглядами: однотипними чи різнотипними. Усі перераховані можливості надаються бібліотекою MFC.

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

У класах вигляду, окрім конструктора, найчастіше використовуються методи:

CDocument* CView::GetDocument()

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

virtual void CView::OnInialUpdate()

- викликається один раз коли вигляд приєднується до документу. В ньому проводиться первинна ініціалізація класу вигляду.

virtual void CView::OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint)

- викликається документом, щоб повідомити вигляду, що в ньому відбулися зміни.

virtual void CView::OnActivateView(BOOL bActivate,

CView *pActivateViev, CView *pDeactivateViev)

- викликається з MFC, коли вигляд активується (отримує фокус вводу) чи деактивується (втрачає фокус вводу).

virtual void CView::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)

- викликається з MFC при прокрутці даних у вигляді.

Клас CView містить одну чисто-віртуальну функцію OnDraw, яка власне і відповідає за промальовування документу на екрані або іншому пристрої.

virtual void CView:: OnDraw (CDC *pDC) = 0

Її необхідно обов’язково перевизначити при створення класу нащадку.

Інші базові класи вигляду забезпечують специфічний інтерфейс для представлення даних у вигляді: тексту - CEditView; списку - CListView; дерева - CTreeView. У MFC реалізовано також і вигляди з розширеними можливостями.

Документ та його вигляд.

4. Програми на основі діалогу.

Тема № 10: Елементи інтерфейсу користувача на основі класів MFC

План

1. Меню.

2. Панелі елементів керування.

3. Елементи керування.

4. Блоки діалогу. Модальні та не модальні діалоги.

Меню

Більшість віконних програм під Windows містять однин або декілька видів меню. За допомогою меню в основному і здійснюється керування програмою. До основних типів меню які реалізовані в бібліотеці MFC відносяться:

- основне меню;

- системне меню;

- контекстне (плаваюче) меню.

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

Елементи меню нижнього рівня мати різні типи та містити ряд модифікаторів:

акселератор – повідомляє користувачу набір клавіш для швидкого виклику команди, що зв’язана з цим підпунктом меню;

стрілка – вказує, що з даним підпунктом меню зв’язане підменю, яке буде розкрито при його виборі;

маркер – підказує, що даний пункт (режим) вже вибраний;

трикрапка – повідомляє користувачу, що при виборі цього пункту з’явиться блок діалогу;

- напівтоновий надпис – означає, що в даний момент цей пункт меню недоступний;

- підкреслена літера – дозволяє швидко перейти до елемента меню, шляхом натискання клавіші з літерою яка підкреслена. Такі літери називаються мнемонічними.

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

Системне меню. Присутнє у вікнах, що мають заголовок та розміщене в правому верхньому куті. У нього входять загальні команди керування програмою, такі як: розгорнути та згорнути вікно; закрити вікно; завершити програму.

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

В MFC для побудови та програмування меню створений клас CMenu, що увібрав у себе спеціальні функції для роботи з меню реалізовані в Win32 API.

Клас CMenu. Інкапсулює дескриптор HMENU та надає розробнику методи для створення та маніпуляції з елементом керування типу меню. Меню може бути створено:

- динамічно;

- за шаблоном з ресурсів;

- за шаблоном створеним в пам’яті.

Розглянемо основні члени класу CMenu.

CMenu::m_hMenu

- містить дескриптор типу HMENU вікна меню, що приєднано до класу.

CMenu::CMenu()

- конструктор, створює об’єкт з порожнім меню, яке далі може бути наповнене відповідними методами класу.

BOOL CMenu::CreateMenu()

- створює порожнє меню як вікно Windows та приєднує його до об’єкту класу.

BOOL CMenu::CreatePopupMenu()

- створює порожнє підменю як вікно Windows та приєднує його до об’єкту класу.

BOOL CMenu::LoadMenu(LPCTSTR lpszResourceName)

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

BOOL CMenu::LoadMenuIndirect(const void * lpMenuTemplate)

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

Наступна група функцій призначена для динамічної зміни складу меню. Відповідно вони застосовуються для наповнення порожнього меню.

BOOL CMenu::AppendMenu(UINT nFlags, UINT nIDNewItem = 0, LPCTSTR lpszNewIt = NULL)

- добавляє новий елемент в кінець меню або підменю з ідентифікатором nIDNewItem та назвою lpszNewItem. nFlags – задає тип та вигляд елементу меню (MF_ENABLED, MF_DISABLED, MF_CHECKED та інше).

BOOL CMenu::InsertMenu(UINT nPosition, UINT nFlags,

UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)

- добавляє новий елемент в меню або підменю в позицію задану параметром nPosition.

BOOL CMenu::ModyfyMenu(UINT nPosition, UINT nFlags,

UINT nIDNewItem = 0, LPCTSTR lpszNewItem = NULL)

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

BOOL CMenu::DeleteMenu(UINT nPosition, UINT nFlags)

- знищує елемент в меню або підменю в позиції заданій параметром nPosition.

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

UINT CMenu::EnableMenuItem(UINT nIDEnableItem, UINT nEnable);

UINT CMenu::CheckMenuItem(UINT nIDCheckItem, UINT nCheck);

UINT CMenu::CheckMenuRadioItem(…);

UINT CMenu::SetMenuItemBitmaps(…);

В класі також реалізовані функції, що дозволяють отримати різноманітну інформацію як про саме меню так і про окремі його елементи.

На закінчення розглянемо функцію за допомогою якої створюється плаваюче меню.

BOOL CMenu::TrackPopupMenu(UINT nFlags, int x, int y,

CWnd *pWnd, LPCRECT lpRect = 0);

- виводить на екран контекстне меню та створює свій цикл обробки повідомлень. Після вибору елементу меню або відмови від вибору меню знищується. pWnd – визначає вікно яке отримає повідомлення WM_COMMAND після вибору користувачем елемента меню.

Панелі елементів керування.

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

Базовим класом для панелей елементів керування є клас CControlBar. Ієрархія класів приведена на рис. __

Рис. __. Ієрархія класів панелей елементів керування

Відповідно до приведеної ієрархії класів, панель керування є вікном, і може містити дочірні вікна, які можуть отримувати та відправляти повідомлення (комбінований список COMBO BOX, текстове поле EDIT) або тільки керуватися програмою (кнопки з малюнком BITMAP BUTTONS, ділянка рядка стану STATUS BAR PANES). Базові функціональні можливості для всіх типів панелей елементів керування подібні – всі їх об’єкти є дочірніми вікнами деякої батьківської рамки.

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

Так як клас CControlBar є абстрактним, об’єктів на основі нього створювати не можна. Панелі керування створюються на основі дочірніх класів. До методів які реалізовані в класі CControlBar та часто використовуються відносяться:

virtual CSize CControlBar::CalcFixedLayout(BOOL bStretch, BOOL Horz);

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

void CControlBar::SetBarStyle(DWORD dwStyle);

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

DWORD CControlBar::GetBarStyle();

- повертає поточний стиль панелі елементів керування.

BOOL CControlBar::IsFloating();

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

З панелями керування тісно зв’язані деякі функції батьківської рамки (класу CFrameWnd), серед них:

void CFrameWnd::SaveBarState(LPCTSTR lpszProfileName);

void CFrameWnd::LoadBarState(LPCTSTR lpszProfileName);

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

void CFrameWnd::ShowControlBar(CControlBar * pBar, BOOL bShow, BOOL bDelay);

- гасить або візуалізує панель елементів керування.

void CFrameWnd::DockControlBar(CControlBar * pBar, …);

- прив’язує панель до певної сторони рамки.

void CFrameWnd::FloatControlBar(CControlBar * pBar, …);

- переводить панель в плаваючий стан.

Як приклад дочірнього класу CControlBar розглянемо панель інструментів CToolBar. У цьому класі реалізовано відображення елементів керування у вигляді кнопок з піктограмами. Інші класи панелей елементів керування організовані подібно, тільки реалізують специфічні для них властивості. Тому розгляд цього класу дасть ключ до розуміння інших класів панелей

Клас CToolBar. Цей клас визначає поведінку панелі інструментів, що задаються кнопками з піктограмами (розміром 23х22 піксела) та розділювачами. Кнопки, подібно до пунктів меню, активізовують команди, що посилаються об’єктам програми та обробляються ними. Кнопки можуть працювати, також, як прапорці та перемикачі.

Для створення об’єкту класу CToolBar використовується конструктор.

void CToolBar::CToolBar();

- конструктор, створює об’єкт класу.

void CToolBar::Create(CWnd * pParentWnd, DWORD Style, UINT nID = AFX_IDW_TOOLBAR);

- створює вікно та приєднує його до об’єкту. pParentWnd – вказівник на батьківське вікно панелі інструментів; Style - стиль панелі; nID – оригінальний ідентифікатор панелі.

void CToolBar::LoadToolBar(LPCTSTR lpszResourceName);

- створює вікно та приєднує його до об’єкту та завантажує дані для нього з ресурсів зданих через ім’я lpszResourceName.

void CToolBar::LoadBitmap(LPCTSTR lpszResourceName);

- завантажує з ресурсів програми бітові масиви з піктограмами (через ім’я lpszResourceName).

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

Елементи керування

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

CStatic – статичний текст або зображення (мітки);

CButton – командні кнопки, що спрацьовують при натисканні, перемикачі, прапорці;

CListBox – список;

CEdit – текстове поле;

CComboBox – комбінований блок списку та текстового поля;

CScrollBar – смуга прокрутки.

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

CBitmapButton (CButton) – кнопки із заданими картинками для всіх станів;

CComboBoxEx (CComboBox) – комбінований список з можливістю роботи із зображеннями;

CCheckListBox (CListBox) – список елементів, що реалізовані як прапорці;

CDragListBox (CListBox) – список з можливістю переносу його елементів за допомогою миші.

При виборі між простим та розширеним елементом керування необхідно вирішувати наскільки повно будуть використовуватися розширені можливості. При неповному їх використанні краще обмежитися простими елементами. Вони простіші в застосуванні та працюють швидше.

Способи створення елементів керування.

Типові елементи керування можна створити двома способами:

- в редакторі ресурсів;

- безпосередньо в коді програми.

Створення елементів керування в редакторі ресурсів. Елементи керування створюються візуально безпосередньо в редакторі ресурсів при проектуванні діалогового вікна. В цьому випадку можна задати розміри, зовнішній вигляд, початковий стан та деяку поведінку цих елементів. Але деякі типи елементів керування (наприклад CListBox, CComboBox) не можна створити повністю повноцінними за допомогою редактора ресурсів. Вони будуть вимагати написання додаткового коду, щоб активізувати усі їх можливості.

Створення елементів керування в коді програми. Як і інші візуальні об’єкти класів MFC, елементи керування в коді програми створюються в два етапи:

- створення об’єкту (екземпляру) певного класу (змінної типу);

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

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

Розглянемо типовий приклад створення елементу керування текстову поле в блоці діалогу.

Спочатку, агрегація включення об’єкту m_Edit в клас CPrimeDialog.

class CPrimeDialog : public CDialog

{

protected:

CEdit m_Edit;

public:

virtual BOOL OnInitDialog();

};

У методі OnInitDialog() для елементу керування текстове поле (m_Edit) задається прямокутник, що задає його розміри та положення в межах вікна діалогу, потім викликається його метод Create(), для створення об’єкту вікна та зв’язування його з базовим об’єктом. На завершення викликається його метод SetFocus(), що захоплює для нього фокус вводу в межах діалогу.

BOOL CPrimeDialog::OnInitDialog()

{

CDialog::OnInitDialog();

CRect winRect(20, 20, 100, 50);

m_Edit.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |

ES_CENTER | ES_LOWERCASE | ES_MULTILINE,

winRect,

this,

IDC_EDIT);

m_Edit.SetFocus();

return FALSE;

}

Метод Create() має ряд параметрів загальних для всіх елементам керування. Розглянемо ці параметри на прикладі відповідного методу для елементу керування класу CEdit – текстове поле.

BOOL CEdit::Create(DWORD dwStyle, const RECT & rect, CWnd * pParentWnd, UINT nID);

Параметри:

dwStyle – задає стилі елемента керування, серед них можуть бути як загальні стилі вікон (WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER), так і особливі стилі притаманні тільки певному класу елементів керування (ES_CENTER | ES_LOWERCASE | ES_MULTILINE). Стилі сумуються за допомогою побітового оператора „або”.

rect – задає габарити та положення, в межах вікна діалогу, прямокутника вікна елементу керування, параметри прямокутника сформовані у вигляді структури RECT. Для розглянутого прикладу лівий верхній кут прямокутника матиме координати (20, 20), а правий нижній (100, 50).

pParentWnd – вказівник на батьківське вікно до якого належить елемент керування. Через нього елемент керування буде обмінюватися повідомлення з іншими елементами інтерфейсу користувача.

nID – ідентифікатор елементу керування, оригінальний в межах батьківського вікна. Він використовується деякими методами класу CWnd для доступу до елементу керування.

Кожен з класів елементів керування може мати свої, додаткові параметри для методу Create(), але перелічені має кожен клас.

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

Блоки діалогу

Блоки діалогу (dialog boxes) – спеціальні вікна, що забезпечують користувача гнучкими засобами взаємодії з програмним забезпеченням. Вони, як правило включають дочірні вікна, що є елементами керування (розглядалися раніше) через які користувач налаштовує програму та водить дані для обробки. Вони є однією з основних та традиційних частин інтерфейсу користувача в Windows.

Модальні та не модальні діалоги.

Відповідно до поведінки, розрізняють два типи діалогів – модальний (modal) та немодальний (modeless). Різниця між ними полягає в способі керування потоками повідомлень.

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

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

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

Блоки діалогів, модальні та немодальні, як і деякі інші вікна, можуть створюватися вдома способами:

- за шаблоном з ресурсів;

- за шаблоном створеним в пам’яті.

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

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

CDialog::CDialog(LPCTSTR lpszTemplateName, CWnd * pParentWnd = NULL);

CDialog::CDialog(UINT nIDTemplate, CWnd * pParentWnd = NULL);

де lpszTemplateName та nIDTemplate – відповідно символьний рядок та число, що ідентифікують діалог в ресурсах; pParentWnd – вказівник на батьківське вікно, якщо він нульовий, то батьківським є основне вікно програми.

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

BOOL CDialog::InitModalIndirect(LPCDLGTEMPLATE lpDialogTemplate,

CWnd * pParentWnd = NULL);

для ініціалізації діалогу.

Діалог створений за першим або другим способом буде невидимим. Для його активізації необхідно викликати метод

virtual int CDialog::DoModal();

який активізує і відобразить діалог (модальний). Ця функція захопить та буде утримувати керування доти, доки діалог не буде закритий. Вона повертає ціле число, через яке можна визначити код завершення діалогу. Наприклад, була натиснута кнопка „Так” чи „Ні”.

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

CDialog::Create(LPCTSTR lpszTemplateName, CWnd * pParentWnd = NULL);

CDialog::Create(UINT nIDTemplate, CWnd * pParentWnd = NULL);

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

BOOL CDialog::CreateIndirect(LPCDLGTEMPLATE lpDialogTemplate,

CWnd * pParentWnd = NULL);

На відміну від модального діалогу – немодальний за замовчуванням створюється невидимим. Для його візуалізації та заковування необхідно явно викликати метод ShowWindow().

Ще одна функціональна відмінність між модальним та немодальним діалогами. Метод DoModal(), створює та знищує об’єкт вікна при кожному своєму запуску. Для немодального діалогу об’єкт вікна, після виклику методу Create(), існує завжди. Для його знищення необхідно явно викликати метод DestroyWindow().

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

Класи типових діалогів Windows.

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

CFileDialog – діалог для навігації системою каталогів та вибору файлу.

CColorDialog – діалог вибору або створення палітри кольору.

CFontDialog – діалог вибору та на лаштування параметрів шрифту.

CFindReplaceDialog – діалог для організації пошуку та заміни тексту.

CPrintDialog – діалог вибору та налаштування друкарки.

CPageSetupDialog – діалог налаштування параметрів сторінки.

Тема № 11: Графічні об’єкти в MFC

План

1. Типи графічних пристроїв та їх контексти.

2. Види графічних об’єктів.

Типи графічних пристроїв та їх контексти

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

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

В бібліотеці MFC реалізовано ряд класів, що беруть на себе більшу частину роботи зі створення, ініціалізації та коректному звільненні графічних ресурсів, що необхідно для ефективної реалізації графічного виводу.

В MFC реалізовано п’ять типів контекстів графічних пристроїв, базовий CDC та чотири похідних від нього CPaintDC, CClientDC, CWindowDC та CMetaFileDC.

CDC – базовий клас для всіх класів, що інкапсулюють контексти пристроїв Windows. Об’єкти цього класу працюють зі всім дисплеєм або з друкаркою в цілому. Вказівник на цей клас передається в такі функції, як наприклад CView::OnDraw(), хоча фактично він може вказувати на довільний дочірній об’єкт.

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

CPaintDC – об’єкти цього класу використовуються тільки в обробнику повідомлень WM_PAINT, що генерується функціями UpdateWindow() та RedrawWindow(). Обробником такого повідомлення є функція CWnd::OnPaint().

CClientDC – об’єкти цього класу забезпечують доступ тільки до клієнтської частини вікна. Використовується для графічного виводу в довільній функції на відміну від об’єктів класу CPaintDC.

CWindowDC – об’єкти цього класу забезпечують доступ до всього вікна, клієнтської та неклієнтської його частин.

CMetaFileDC – об’єкти цього класу забезпечують доступ до метафайлів Windows. Метафайли можуть бути як контекстно-залежними так контекстно-незалежними. В другому випадку дані які записані в них можуть бути виведені на довільний фізичний пристрій однаково.

Розглянемо базовий клас контекстів пристроїв CDC, так як основні функціональні можливості реалізовано саме в ньому.

Основною функції ініціалізації об’єкту класу CDC є:

virtual BOOL CDC::CreateDC(LPCTSTR lpszDriverName, LPCTSTR lpszDeviceName,

LPCTSTR lpszOutput, const void lpInitData);

де lpszDriverName та lpszDeviceName – символьні рядки з назвами драйверу та конкретного фізичного пристрою; lpszOutput – символьне ім’я потоку виводу (файлу чи порту); lpInitData – вказівник на структуру, що містить нилаштування пристрою.

Для створення контексту пристрою в пам’яті використовується функція

virtual BOOL CDC::CreateCompatibleDC(CDC * pDC);

вивід відбувається в пам’ять, не безпосередньо на пристрій. Після формування картинки повністю її можна направити на фізичний пристрій, що створює ефект миттєвості.

Робота з об’єктами малювання. Для роботи з об’єктами малювання в контексті пристрою передбачені функції, що дозволяють встановити відповідний об’єкт та отримати вказівник на відповідний встановлений об’єкт малювання. До перших відносяться перевантажувана функція CDC::SelectObject(), наприклад:

CPen * CDC::SelectObject(CPen * pPen);

CBrush * CDC::SelectObject(CBrush * pBrush);

До другої групи функцій належать:

CPen * CDC::GetCurrentPen();

CBrush * CDC::GetCurrentBrush();

CBitmap * CDC::GetCurrentBitmap();

CFont * CDC::GetCurrentFont();

Крім встановлення об’єктів малювання функціями контексту пристрою встановлюється:

режими малювання – замальовування фону, заповнення багатокутників, стиск/розтягування бітових образів;

режими відображення – налаштування системи координат, встановлення початку координат, встановлення порту огляду, співвідношення масштабів за осями координат, перетворення системи координат та інше;

функції малювання – відсічення багатокутників, малювання ліній, відображення багатокутників та інше;

способи відображення бітових масивів;

способи відображення тексту.

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

Види графічних об’єктів.

Базовим класом для шести графічних об’єктів Windows CPen, CBrush, CBitmap, CFont, CPalette та CRgn є CGdiObject. Він містить набір функцій загальних для всіх графічних об’єктів. Він містить член класу CGdiObject::m_hObject в якому зберігається дескриптор одного з шести графічних об’єктів.

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

CPen (олівець) – малювання ліній та контурів з різними стилями, властивостями та типами;

CBrush (пензлик) – основа для замальовування замкнутих фігур, можуть бути однотонними, тонованими, штрихованими та створеними на основі шаблону;

CFont (шрифт) – задає тип, стиль та вигляд шрифту;

CBitmap (бітовий масив) – забезпечує набір функції для роботи з растровими картинками;

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

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

Контрольні запитання

До модулю 1.

1. 

До модулю 2.

1. 

Екзаменаційні.

1. 

1

Смотреть полностью


Скачать документ

Похожие документы:

  1. Програми для загальноосвітніх навчальних закладів. Запоріжжя: Прем'єр, 2003. 304 с. I8Вn 966-685-066-4

    Документ
    У збірнику вміщено програми з інформатики для загальноосвітніх навчальних закладів різного профілю, для спеціалізованих шкіл, гімназій, ліцеїв з поглибленим вивченням інформатики, програми факультативів, спецкурсів, пропедевтичних курсів та гуртків.
  2. Програма іспиту з професійно-орієнтованих дисциплін кваліфікації «Бакалавр комп’ютерних наук» (2)

    Документ
    Стюарт Расел, Пітер Норвіг (ssell, P.Norvig) Штучний інтелект: Сучасний підхід. 2-е видання.: Пер. з англ. / М.: Вид-во «Діалектіка-Вільямс» – 2007 р.
  3. І. Б. Трегубенко Г. Т. Олійник О. М. Панаско Сучасні технології програмування в мережах

    Документ
    Для засвоєння студентами базових технологій програмування в мережах у теоретичному аспекті розглядаються питання ієрархічної структури об’єктів, об’єктно-орієнтованих концепцій програмування, роботи з формами та фреймами та ін.
  4. Програми для загальноосвітніх навчальних закладів Навчальні програми для профільного навчання (1)

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

    Документ
    головний спеціаліст Головного управління змісту освіти Міністерства освіти і науки України Н. Прокопенко, завідувачка сектору Науково-методичного центру середньої освіти Міністерства освіти і науки України О.

Другие похожие документы..