Закінчена програма h2>
Розберемо процес написання програми для малювання на
екрані геометричних фігур. Вона природним чином поділяється на три частини:
p>
Адміністратор екрану: підпрограми низького рівня і
структури даних, що визначають екран; він відає тільки точками і прямими
лініями; p>
Бібліотека фігур: набір визначень основних фігур на зразок
прямокутника і кола і стандартні програми для роботи з ними, а p>
Прикладна програма: безліч визначень, спеціалізованих
для даного продукту, і код, в якому вони використовуються. p>
Ці три частини швидше за все будуть писати різні люди (в
різних організаціях і в різний час). При цьому частини будуть швидше за все писати
саме в зазначеному порядку з тим ускладнює обставиною, що у
розробників нижнього рівня не буде точного уявлення, для чого їх код в
кінцевому рахунку буде використовуватися. Це відображено у що наводиться прикладі. Щоб
приклад був коротший, графічна бібліотека надає тільки досить обмежений
сервіс, а сама прикладна програма дуже проста. Щоб читач зміг випробувати
програму, навіть якщо у нього немає зовсім ніяких графічних засобів,
використовується надзвичайно проста концепція екрану. Не повинно скласти праці
замінити цю екранну частина програми чим-небудь придатним, не змінюючи код
бібліотеки фігур і прикладної програми. p>
Адміністратор Екрану b> p>
Спочатку був намір написати адміністратор екрана на C
(а не на C + +), щоб підкреслити поділ рівнів реалізації. Це виявилося
занадто втомливим, тому довелося піти на компроміс: Стиль
C (немає функцій членів, віртуальних функцій, що визначаються користувачем операцій
тощо), однак застосовуються конструктори, належним чином описуються і
перевіряються параметри функцій і т.д. Озираючись назад, можна сказати, що
адміністратор екрана дуже схожий на C програму, яку потім модифікували,
щоб скористатися засобами C + + не переписуючи все повністю. p>
Екран представляється як двовимірний масив символів,
роботу з яким здійснюють функції put_point () і put_line (), що використовують
при посиланні на екран структуру point: p>
// фото screen.h p>
const XMAX = 40, YMAX = 24; p>
struct point ( p>
int x, y; p>
point () () p>
point (int a,
int b) (x = a; y = b;) p>
); p>
overload put_point; p>
extern void put_point (int a, int b); p>
inline void put_point (point p) (put_point (px, py);) p>
overload put_line; p>
extern void put_line (int, int, int, int); p>
inline void put_line (point a, point b) p>
(
put_line (a.x, a.y, b.x, b.y);) p>
extern void screen_init (); p>
extern void screen_refresh (); p>
extern void screen_clear (); p>
# include p>
Перед першим використанням функції put екран треба
ініціалізувати за допомогою screen_init (), а зміни в структурі даних екрана
відображаються на екрані тільки після виклику screen_refresh (). Як побачить
користувач, це "оновлення" ( "refresh") здійснюється
просто за допомогою друку нової копії екрану під його попереднім варіантом. Ось
функції і визначення даних для екрану: p>
# include "screen.h" p>
# include p>
enum color (black ="*", white = ""
); p>
char screen [XMAX] [YNAX]; p>
void screen_init () p>
( p>
for (int y = 0;
y = a | | a <= b) y0 + = dy, eps -= two_a; p>
) p>
) p>
Надаються функції для очищення екрана і його
оновлення: p>
void screen_clear () (screen_init ();)// очищення p>
void screen_refresh ()// оновлення p>
( p>
for (int y = YMAX-1; 0 <= y; y -) (//зверху вниз p>
for (int x = 0; x p>
Бібліотека Фігур h2>
Нам потрібно визначити загальне поняття фігури (shape). Це
треба зробити таким чином, щоб воно використовувалося (як базовий клас) усіма
конкретними фігурами (наприклад, колами і квадратами), і так, щоб будь-який
фігурою можна було маніпулювати виключно через інтерфейс,
що надається класом shape: p>
struct shape ( p>
shape () (shape_list.append (this);) p>
virtual point north () (return point (0,0);)// північ p>
virtual point south () (return point (0,0);)// південь p>
virtual point east () (return point (0,0);)// схід p>
virtual point neast () (return point (0,0);)// північний схід p>
virtual point seast () (return point (0,0);)// південний схід p>
virtual void draw () ();// намалювати p>
virtual void move (int, int) ();// перемістити p>
); p>
Ідея полягає в тому, що розташування фігури задається за
допомогою move (), і фігура поміщається на екран за допомогою draw (). Фігури можна
мати у своєму розпорядженні відносно один одного, використовуючи поняття точки дотику, і
ці точки перераховуються після точок на компасі (сторін світу). Кожна
конкретна фігура визначає свій сенс цих точок, і кожна визначає спосіб,
яким вона малюється. Для економії місця тут насправді визначаються
тільки необхідні в цьому прикладі сторони світу. Конструктор shape:: shape ()
додає фігуру в список фігур shape_list. Цей список є gslist, то
Тобто, одним з варіантів узагальненого односвязанного списку, визначеного в
# 7.3.5. Він і відповідний ітератор були зроблені так: p>
typedef shape * sp; p>
declare (gslist, sp); p>
typedef gslist (sp) shape_lst; p>
typedef gslist_iterator (sp) sp_iterator; p>
тому shape_list можна описати так: p>
shape_lst shape_list; p>
Лінію можна побудувати або по двох точках, або по точці
і цілому. В останньому випадку створюється горизонтальна лінія, довжину якої
визначає ціле. Знак цілого вказує, яким кінцем є точка: лівим або
правим. Ось визначення: p>
class line: public shape ( p>
/* p>
лінія з "w" в "e" p>
north () визначається як `` вище центру p>
і на північ як до самої північної точки "" p>
*/ p>
point w, e; p>
public: p>
point north () p>
(return point ((w.x + e.x)/2, e.ydraw (); p>
screen_refresh (); p>
) p>
І ось, нарешті, справжня сервісна функція (утиліта).
Вона кладе одну фігуру на верх інший, задаючи, що south () однієї повинен бути відразу
над north () інший: p>
void stack (shape * q, shape * p)// ставить p на верх
q p>
( p>
point n = p-> north (); p>
point s = q-> south (); p>
q-> move (n.x-s.x, n.y-s.y +1); p>
) p>
Тепер уявімо собі, що ця бібліотека вважається
власністю якоїсь компанії, яка продає програмне забезпечення, і що
вони продають вам тільки заголовки, що містить визначення фігур, і
скомпільованій варіант визначень функцій. І у вас все одно залишається
можливість визначати нові фігури та будуть використані для ваших власних фігур
сервісні функції. p>
Прикладна Програма h2>
Прикладна програма надзвичайно проста. Визначається
нова фігура my_shape (на печатці вона трохи схожа на пику), а потім пишеться
головна програма, яка надягає на неї капелюха. Спочатку опис my_shape: p>
# include "shape.h" p>
class myshape: public rectangle ( p>
line * l_eye;// ліве око p>
line * r_eye;// праве око p>
line * mouth;// рот p>
public: p>
myshape (point, point); p>
void draw (); p>
void move (int, int); p>
); p>
Очі і рот - окремі і незалежні об'єкти, які
створює конструктор my_shape: p>
myshape:: myshape (point a, point b): (a, b) p>
( p>
int ll = neast (). x-swest (). x 1; p>
int hh = neast (). y-swest (). y 1; p>
l_eye = new line ( p>
point (swest (). x 2, swest (). y + hh * 3/4), 2); p>
r_eye = new line ( p>
point (swest (). x + ll-4, swest (). y + hh * 3/4), 2); p>
mouth = new line ( p>
point (swest (). x 2, swest (). y + hh/4), ll-4); p>
) p>
Об'єкти очі і рот порізно малюються заново функцією
shape_refresh (), і в принципі можуть оброблятися незалежно з об'єкта
my_shape, якій вони належать. Це один спосіб визначати засоби для
ієрархічно побудованих об'єктів на кшталт my_shape. Інший спосіб демонструється
на прикладі носа. Ніякої ніс не визначається, його просто додає до зображення
функція draw (): p>
void myshape:: draw () p>
( p>
rectangle:: draw (); p>
put_point (point ( p>
(swest (). x + neast (). x)/2, (swest (). y + neast (). y)/2 )); p>
) p>
my_shape пересувається за допомогою переміщення базового
прямокутника rectangle і вторинних об'єктів l_eye, r_eye і mouth (лівого
очі, правого ока і рота): p>
void myshape:: move () p>
( p>
rectangle:: move (); p>
l_eye-> move (a, b); p>
r_eye-> move (a, b); p>
mouth-> move (a, b); p>
) p>
Ми можемо, нарешті, побудувати кілька фігур і трохи їх
посувати: p>
main () p>
( p>
shape * p1 = new rectangle (point (0,0), point (10,10 )); p>
shape * p2 = new line (point (0,15), 17); p>
shape * p3 = new myshape (point (15,10), point (27,18 )); p>
shape_refresh (); p>
p3-> move (-10, -10); p>
stack (p2, p3); p>
stack (p1, p2); p>
shape_refresh (); p>
return 0; p>
) p>
Ще раз зверніть увагу, як функції начебто
shape_refresh () і stack () маніпулюють об'єктами типів, що визначаються набагато
пізніше, ніж були написані (і, може бути, відкомпілювати) самі ці функції. p>
Результатом роботи програми буде: p>
*********** p>
*
* p>
*
* p>
*
* p>
*
* p>
*
* p>
*
* p>
*
* p>
*
* p>
*
* p>
*********** p>
***************** p>
************* p>
* * p>
* ** ** * p>
* * p>
* *
* p>
* * p>
* ********* * p>
* * p>
************* p>
Список літератури h2>
Для підготовки даної роботи були використані матеріали
з сайту http://www.realcoding.net
p>