Скачиваний:
82
Добавлен:
02.05.2014
Размер:
2.28 Mб
Скачать

19.8. Является ли окружность эллипсом

До сих пор в этой главе подразумевалось, причем довольно обоснованно, что окруж- ности являются эллипсами. Однако необходимо обратить ваше внимание, что в литера- туре ведутся горячие споры по этому тривиальному вопросу. Рассмотрим наши обычные переменные Е и С, имеющие объявленные типы ELLIPSE и CIRCLE соответственно. Пред- положим, что эти переменные инициализированы следующим образом.

.Е := ELLIPSE ( LENGTH ( 5.0 ), LENGTH { 3.0 j,

POINT ( 0.0, 0.0 )) ; С := CIRCLE ( LENGTH ( 5.0 ), POINT ( 0.0, 0.0 )) ?

Обратите внимание, что оба оператора, ТНЕ_А(С) и ТНЕ_В(С), в данном случае имеют значение 5.

Теперь можно выполнить операцию с переменной Е, например "обновить значение длины полуоси а".

ТНЕ_А ( Е ) := LENGTH ( 6.0 ) ;

А что будет, если попытаться выполнить аналогичную операцию с переменной С? ТНЕ_А ( С ) := LENGTH ( 6.0 ) ;

Мы получим ошибку! Что же это за ошибка? Если бы обновление все-таки произош- ло, то переменной С предстояло бы содержать "окружность", которая нарушает ограни- чение для окружностей, согласно которому а = Ь (значение а было бы равно б, а значе- ние Ь, наверное, оставалось бы равным 5, поскольку мы его не изменяли). Иными слова- ми, переменная С содержала бы "некруглую окружность" и, таким образом, было бы на- рушено ограничение для типа CIRCLE.

Поскольку "некруглые окружности" противоречат логике и здравому смыслу, навер- ное, разумно предложить, чтобы такое обновление было недопустимо. Также очевидно, что достичь этого можно во время компиляции, отбрасывая все подобные операции. Для этого достаточно определить задаваемую оператором присвоения операцию обновления так, чтобы отдельное обращение к полуосям а и b для окружностей было синтаксически

недопустимо. Иначе говоря, операторы присвоения ТНЕ_А и ТНЕ_В должны быть непри- менимы для типа CIRCLE, а попытка подобного обновления должна приводить к ошибке несовпадения типа во время компиляции.

Замечание. В действительности "очевидно", что подобные операторы присвоения должны быть синтаксически недопустимыми. Напомним, что присвоение значения с помощью оператора ТШ_псевдопеременная— это просто сокращение. Поэтому, напри- мер, попытка выполнения операции присвоения с оператором THE А, показанная выше, если бы она была допустима, могла бы быть переписана в таком виде.

С := CIRCLE (...);

Но вызов оператора выбора для типа CIRCLE в правой части оператора присвоения должен был бы включать как аргумент типа ТНЕ_А с новой длиной полуоси а, равной LENGTH(6,0). Однако оператор выбора для типа CIRCLE не принимает аргумента типа ТНЕ_А — он принимает аргументы типов THE_R и THE_CTR. Таким образом, становится ясно, что исходное присвоение должно быть недопустимым!

Об изменении семантики

Учитывая сказанное выше, сразу же отбросим предложение, суть которого заключа- ется в попытке реанимировать идею, состоящую в том, что операторы присвоения THE А и ТНЕ_В должны быть допустимы для окружностей. Обычно предлагается, чтобы, напри- мер, оператор ТНЕ_А был переопределен, иначе говоря, явно специализирован, для окруж- ностей таким образом, чтобы иметь также побочный эффект присвоения соответствую- щего значения для оператора ТНЕ_В, так что после обновления окружность по-прежнему будет удовлетворять ограничению а = Ь. Мы отвергаем это предложение по крайней мере по следующим трем причинам.

  • Во-первых, семантика присвоения для операторов ТНЕ_А и ТНЕ_В предписана на- шей моделью наследования и не должна изменяться предлагаемым способом.

  • Во-вторых, даже если бы эта семантика присвоения не была предписана нашей моделью, мы уже доказали, что изменение семантики оператора произвольным образом — это, вообще говоря, не лучшая идея, а изменение семантики оператора таким способом, который приводит к побочным эффектам— это еще худшая идея. Лучше придерживаться общего принципа, который гласит, что операторы должны делать то, что от них требуется, не больше и не меньше.

  • И, в-третьих, что наиболее важно, изменение семантики предлагаемым способом не всегда возможно. Например, пусть тип ELLIPSE имеет другой непосредствен- ный подтип NONCIRCLE, и пусть ограничение а > b применимо для подобных "неокружностей". Рассмотрим оператор присвоения ТНЕ_А для "неокружности", который устанавливал бы длину полуоси а равной Ь. Какое тогда должно быть со- ответствующее семантическое переопределение для этого присвоения? Какой именно побочный эффект был бы подходящим в этом случае?

Существует ли гибкая модель

Итак, мы остались на позициях, когда операторы присвоения ТНЕ_А и ТНЕ_В приме- нимы для эллипсов вообще, но не применимы для окружностей в частности. Однако до- полнительно необходимо учесть следующее.

  1. Предполагается, что тип CIRCLE является подтипом типа ELLIPSE.

  2. Предполагается, что утверждение, согласно которому тип CIRCLE является подти- пом типа ELLIPSE, подразумевает, что операции, которые применимы к эллипсам вообще, применимы (т.е. наследуются), в частности, и к окружностям.

  3. Однако сейчас мы требуем, чтобы операции присвоения для операторов ТНЕ_А и ТНЕ_В не наследовались.

Нет ли здесь противоречия? Что же произошло?

Прежде чем попытаться ответить на эти вопросы, подчеркнем серьезность данной про- блемы. Если определенные операторы не наследуются типом CIRCLE от типа ELLIPSE, то что мы имеем в виду, когда говорим, что окружность "является" эллипсом? Что означает "наследование", если некоторые операторы на самом деле не наследуются? Существует ли действительно гибкая модель? Или же мы гоняемся за призраком, пытаясь его поймать?

Замечание. Некоторые авторы даже предлагают, причем вполне серьезно, применять оператор присвоения ТНЕ_А как для окружностей, так и для эллипсов (для окружностей обновляется радиус), а оператор присвоения ТНЕ_В — только для эллипсов. Но тогда тип ELLIPSE фактически должен стать подтипом типа CIRCLE! Иными словами, мы получим перевернутую иерархию типов. Но достаточно хоть на минуту задуматься, чтобы понять, что это неудачная идея, в частности концепция заменимости в этом случае будет разру- шена (что такое радиус обычного эллипса?).

Одних авторов рассуждения, аналогичные рассмотренным выше, приводят к выводу, что гибкой модели наследования не существует (см. аннотацию к [19.1] в разделе "Список литературы" в конце главы). Другие же авторы предлагают модели наследова- ния, свойства которых нелогичны или весьма сомнительны. Например, в языке SQL3 разрешены "некруглые окружности" и другие нелепости. Как и язык SQL/92, язык SQL3 фактически не поддерживает ограничений типа, и это большое упущение, в первую оче- редь, приводит к тому, что подобные нелепости имеют место (см. приложение Б).

Решение проблемы

Исходя из сказанного выше можно сделать вывод, что мы столкнулись со следующей дилеммой.

  • Если окружности наследуют операторы присвоения ТНЕ_А и ТНЕ_В от эллипсов, мы получим некруглые окружности.

  • Чтобы предотвратить возникновение некруглых окружностей, необходима под- держка ограничений типа.

  • Но, если мы поддерживаем ограничения типа, операторы не могут быть на- следуемыми.

■ Таким образом, в результате никакого наследования вовсе не может быть! Как же решить эту дилемму?

Выход заключается (как это часто бывает) в необходимости осознать соответст- венно действовать), что существует значительное логическое различие между значе- ниями и переменными. Говоря "Каждая окружность является эллипсом", мы подразу- меваем, что значение каждой окружности является значением эллипса. Мы, безусловно, не считаем при этом, что каждая переменная окружности является некоторой переменной

эллипса (переменная объявленного типа CIRCLE не является переменной объявленного типа ELLIPSE, и она не может содержать значение конкретного типа ELLIPSE). Иначе го- воря, наследование применимо к значениям, а не к переменным. В случае эллипсов и окружностей, например, можно сформулировать следующие правила.

  • Как уже отмечалось, значение каждой окружности является значением эллипса.

  • Следовательно, все операции, которые применимы к эллипсу, применимы и к ок- ружности.

  • Единственное, чего мы не можем делать по отношению к любым значениям, — это изменять их! Если бы это было возможно, они бы перестали быть значения- ми. (Конечно, можно изменить "текущее значение" переменной путем ее обновле- ния, но, повторяем, мы не можем изменить значение как таковое.)

Операции, которые применимы к значениям эллипса, все без исключения являются операциями, определенными для типа ELLIPSE и предназначенными лишь для чтения. А операции, которые обновляют переменные типа ELLIPSE, представляются, конечно, операторами обновления, определенными для этого типа. Следовательно, наше утвер- ждение, что "наследование применимо к значениям, а не к переменным", точнее мож- но сформулировать так.

Операторы, предназначенные лишь для чтения, наследуются с помощью значений, в том числе с помощью текущих значений переменных. Поэтому данные операторы могут быть применены без каких-либо нежелательных послед- ствий к значениям, которые являются текущими значениями переменных.

Эта более точная формулировка также объясняет, почему концепции полиморфизма и заменимости относятся именно к значениям, а не к переменным. Напомним, что в соот- ветствии с концепцией заменимости везде, где системой должно обрабатываться значе- ние типа Т, вместо него можно подставить значение типа Т', если тип Т' — подтип типа Т. И именно этот принцип рассматривался, когда мы впервые представляли его как прин- цип заменимости значений.

А как же тогда быть с операторами обновления? По определению такие операторы при- менимы к переменным, но не к значениям. Можем ли мы тогда сказать, что операторы, кото- рые применимы к переменным типа ELLIPSE, наследуются и переменными типа CIRCLE?

Нет, в целом, этого сказать нельзя. Например, оператор присвоения THE_CTR приме- ним к переменным обоих объявленных типов, но, как мы уже убедились, оператор при- своения ТНЕ_А к ним не применим. Таким образом, наследование не распространяется на операторы обновления, точнее — наследуемые операторы обновления должны указы- ваться явно. Рассмотрим пример.

  • Переменные объявленного типа ELLIPSE имеют операторы обновления MOVE (версия обновления) и операторы присвоения ТНЕ_А, THE В и THE_CTR.

  • Переменные объявленного типа CIRCLE имеют операторы обновления MOVE (версия обновления) и операторы присвоения THE_CTR и THE_R, но не ТНЕ_А и ТНЕ_В.

Замечание. Оператор MOVE рассматривался в предыдущем разделе. Безусловно, в случае, когда наследуется и оператор обновления, мы имеем дело с таким видом полиморфизма и таким видом заменимости, которые применимы

также к переменным, а не только к значениям. Например, если для версии обнов- ления оператора MOVE в качестве аргумента может быть подставлена переменная объявленного типа ELLIPSE, можно обратиться к этому оператору и с аргументом, который представляет переменную объявленного типа CIRCLE. Следовательно, можно (и нужно) говорить о принципе заменимости переменных, но при этом не забывать, что данный принцип более ограниченный по сравнению с принципом заменимости значений.

Соседние файлы в папке Дейт К. Дж. Введение в системы баз данных [7 издание]