ОПЕРАТОРИ, ВЕРСІЇ І сигнатури

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

OPERATOR MOVE ( E ELLIPSE, R RECTANGLE ) RETURNS ELLIPSE VERSION ER_MOVE

RETURN ( ELLIPSE ( THE_A ( E ) , THE_B ( E ) ,

R_CTR ( R ) ) )

; END OPERATOR

Оператор MOVE, неформально висловлюючись, пересуває еліпс Е таким чином, щоб центр його збігався з центром прямокутника R або, точніше, він повертає такий же еліпс, як і еліпс, заданий у вигляді фактичного параметра, відповідного формального параметру Е, за винятком того, що центр його знаходиться в центрі прямокутника, заданого в якості фактичного параметра, відповідного формального параметру R Зверніть увагу на специфікацію версії VERSION у другому рядку, в якій вводиться помітне імя ER_MOVE для даної конкретної версії MOVE (незабаром буде визначена ще одна версія цього оператора) Слід також зазначити, що мається на увазі наявність оператора R_CTR, який повертає координати центру зазначеного прямокутника

Тепер визначимо ще одну версію оператора MOVE, призначену для переміщення кіл, а не елліпсов10, яка наведена нижче

OPERATOR MOVE (С CIRCLE, R RECTANGLE) RETURNS CIRCLE VERSION CR_MOVE

RETURN (CIRCLE (THE_R (С), R_CTR (R)))

; END OPERATOR

Аналогічним чином може бути визначена версія оператора MOVE для того випадку, коли фактичні параметри відносяться, відповідно, до найбільш конкретним типам ELLIPSE і SQUARE (скажімо, ES_MOVE), і ще одну версію для того випадку, коли фактичні параметри, відповідно, відносяться до найбільш конкретним типам CIRCLE І SQUARE (скажімо, CS_MOVE)

10 У даному конкретному прикладі визначення такої версії фактично має мало сенсу (поясніть,

чому)

Сигнатури

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

а) фактичні параметри і формальні параметри

б) оголошений тип і найбільш конкретний тип

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

Фактично можна провести різницю між щонайменше трьома різновидами сигнатур, які повязані з будь-яким конкретним оператором Ор (хоча в літературі така відмінність часто не враховується), – Унікальна сигнатура специфікації (specification signature), безліч сигнатур версій (version signature) і безліч сигнатур викликів (invocation signature) Ці три різновиди сигнатур докладно описані нижче

■ Унікальна сигнатура специфікації складається з імені оператора Ор поряд з оголошеними типами, взятими в тому ж порядку, що і формальні параметри оператора Oр, які задані користувачем в оголошенні оператора Ор Така сигнатура відповідає оператору Ор, що розглядається в тому вигляді, в якому він виглядає з точки зору користувача Наприклад, сигнатурою специфікації для наведеного вище оператора MOVE є просто MOVE (ELLIPSE, RECTANGLE)

Примітка У [33] викладена пропозиція, щоб була передбачена можливість відокремити визначення сигнатури специфікації для будь-якого конкретного оператора від визначень всіх версій реалізації цього оператора Основна ідея полягає в тому, що повинна бути забезпечена підтримка обєднаних типів (Іноді званих також абстрактнимиабо неконкретізіруемимі типами, а іноді просто інтерфейсами) під цим маються на увазі типи, які взагалі не можуть стати найбільш конкретним типом будь-якого значення Такий тип надає спосіб визначення операторів, застосовуваних до кількох різних звичайним типам, притому що всі вони є строгими підтипами розглянутого обєднаного типу У такому випадку версії реалізації такого оператора можуть бути визначені для кожного з цих звичайних підтипів Якщо мова йде про приклад, який розглядається протягом всієї даної глави, то в цьому сенсі в якості обєднаного типу цілком може розглядатися PLANE_FIGURE в такому випадку сигнатура специфікації оператора AREA цілком може бути визначена на рівні типу PLANE_FIGURE, ЩО дає можливість після цього визначити явні версії реалізації для типу ELLIPSE, типу POLYGON і тд

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

Глава 20 Спадкування типів 797

версії Ці сигнатури відповідають різним фрагментам коду реалізації, в якому реалізовані версією оператора Ор, приховані від користувача Наприклад, сигнатурою версії для версії CR_MOVE оператора MOVE повинна бути MOVE (CIRCLE, RECTANGLE)

Кожне можливе поєднання найбільш конкретних типів фактичних параметрів має свою власну сигнатуру виклику, що складається з імені оператора Ор поряд з узятими в тому ж порядку найбільш конкретними типами фактичних параметрів Ці сигнатури відповідають всіх можливих викликів оператора Ор (безумовно, що це відповідність відноситься до типу один до багатьох; це означає, що одна сигнатура виклику може відповідати багатьом фактичним викликам) Наприклад, припустимо, що змінні Е І R мають, відповідно, найбільш конкретні типи CIRCLE і SQUARE Тоді сигнатурою виклику для виклику MOVE (Е, R) оператора MOVE є MOVE (CIRCLE, SQUARE)

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

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

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

Порівняння операторів, які забезпечують тільки читання, і операторів поновлення

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

OPERATOR MOVE ( E ELLIPSE, R RECTANGLE ) UPDATES E VERSION ER_MOVE

THE_CTR ( E ) := R_CTR ( R

) END OPERATOR

(Слід нагадати, що оператори, що забезпечують тільки читання, і оператори поновлення іноді іменуються, відповідно,спостерігачамиімодифікаторами Додаткові відомості про те, в чому полягають відмінності між цими різновидами операторів, наведені в розділі 5)

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

Зміна семантики оператора

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

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

шосе) – суворий підтип типу HIGHWAY (Шосе), a TRAVEL_TIME (Час проїзду) –

оператор, який обчислює кількість часу, необхідне для проїзду між двома зазначеними пунктами на зазначеному шосе Для платного шосе ця формула виглядає як (d / s) + (n * t), де d – відстань, s – швидкість, п ​​- кількість пунктів збору плати за проїзд і t – час, що проводиться в кожному пункті збору На відміну від цього, для безкоштовного шосе ця формула виглядає просто як d / s Зміна семантики дозволяє використовувати один і той же оператор TRAVEL_TIME для шосе обох типів

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

11 Як було зазначено у виносці 10, така версія фактично не була потрібна і в разі оператора, що забезпечує тільки читання вона була приведена виключно для використання в розглянутому прикладі

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

1 Визначено тип ELLIPSE і відповідна версія оператора AREA Для спро щення припустимо, що в коді AREA не використовується фізичне уявлення для еліпсів

2 Визначено тип CIRCLE як підтип типу ELLIPSE, але (ще) не визначено окремих ная версія реалізації оператора AREA для кіл

3 Викликається оператор AREA із зазначенням деякої конкретної кола з для отримання результату, скажімо, al У цьому виклику використовується версія оператора AREA, призначена для типу ELLIPSE (оскільки це – єдина версія, яка існує в даний час)

4 Потім буде визначена окрема версія реалізації оператора AREA для окруж ностей

5 Знову викликається оператор AREA Із зазначенням тієї ж конкретної кола з для отримання результату, скажімо, а2 (і на цей раз викликається саме та версія оператора AREA, яка відноситься до типу CIRCLE)

В даний момент, безумовно, бажано, щоб дотримувалася умова а2 = al Але такому необовязкового побажанню не можна надати силу закону це означає, що (як уже було сказано вище) завжди існує така можливість, що версія оператора AREA, реалізована для використання з колами, може повертати (скажімо) периметр замість площі або просто неправильне значення площі

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

■ Якщо тип TOLL_HIGHWAY дійсно є підтипом HIGHWAY, це по оп ределению означає, що кожне окреме платне шосе фактично представля ет собою просто шосе

■ Тому деякі шосе (тобто деякі значення типу HIGHWAY) дійсно представляють собою платні шосе – на них встановлені пункти збору плати за проїзд Тому сам тип HIGHWAY не можна назвати типом, який описує шосе без пунктів збору плати за проїзд; він описує шосе з п пунктами збору плати за проїзд (Де п може дорівнювати нулю)

■ Тому оператор TRAVEL_TIME для типу HIGHWAY не «обчислює час проїзду по шосе без пунктів збору плати за проїзд, а обчислює час проїзду d / s по шосе без обліку пунктів збору плати за проїзд .

■ На відміну від нього, оператор TRAVEL_TIME ДЛЯ типу TOLL_HIGHWAY призначений для обчислення часу проїзду (d / s) + (n * t) по шосе з урахуванням пунктів збору плати за проїзд. Тому насправді два оператора TRAVEL_TIME яв ляють логічно різними У даному випадку плутанина виникла через те, що двом різним операторам присвоєно одне і те ж імя фактично в даному випадку

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

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

Джерело: Дейт К Дж, Введення в системи баз даних, 8-е видання: Пер з англ – М: Видавничий дім «Вільямс», 2005 – 1328 с: Ил – Парал тит англ

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*