|
Программирование >> Хронологические базы данных
Напомним, что эллипсы и окружности (по крайней мере, как мы их определили в разделе 19.2) имеют различные представления. TYPE ELLIPSE POSSREP ( А LENGTH, В LENGTH, CTR POINT )... ; TYPE CIRCLE POSSREP ( R LENGTH, CTR POINT )... ; Поэтому, возможно, могут существовать, хотя это и скрыто от пользователя, две различные версии оператора AREA: одна позволяет использовать представление ELLIPSE, а другая - представление CIRCLE. Повторим, это возможно, но в этом может и не быть необходимости. Например, код оператора для эллипса может выглядеть таким образом. OPERATOR AREA ( Е ELLIPSE ) RETURNS ( AREA ) ; RETURN ( 3.14159 * THE A ( E ) * THE B ( E ) ) ; END OPERATOR ; (Площадь эллипса равна Tiab.) Этот код, очевидно, дает правильный результат и в том случае, если вызывается для вычисления площади окружности, а не эллипса. Операторы ТНЕ А и ТНЕ В возвращают радиус г. Однако программист, ответственный за определение типа CIRCLE, может по разным соображениям предпочесть реализацию отдельной версии оператора AREA, которая предназначена конкретно для окружностей и в которой вызывается оператор THE R вместо операторов ТНЕ А и ТНЕ В. Замечание. На самом деле может быть желательной реализация двух версий оператора, даже если возможные представления одни и те же, в частности по соображениям эффективности. Рассмотрим, например, многоугольники и прямоугольники. Алгоритм вычисления площади многоугольников годится, конечно, и для прямоугольников, но все-таки для прямоугольников более эффективно вычислять площадь как произведение их щирины и высоты. Однако отметим, что код для реализации вычисления площади фигуры типа ELLIPSE не будет пригодным для окружностей, если он написан в терминах реального представления типа ELLIPSE вместо возможного, поскольку реальные представления для типов ELLIPSE и CIRCLE отличаются. И вообще, практика реализации операторов в терминах реальных представлений - это не лучщий подход. Кодируйте надежно! В случае, если код оператора AREA, используемый для типа ELLIPSE, не нужно переписывать заново для типа CIRCLE, можно говорить о повторном использовании кода (т.е. кода, реализующего оператор AREA). Замечание. В следующем подразделе речь пойдет о более важном виде повторного использования. С точки зрения модели, конечно, не имеет никакого значения, сколько версий оператора AREA неявно реализовано (что касается пользователя, то для него есть просто один оператор AREA, который применим для эллипсов, а следовательно, по определению - и для окружностей). Иными словами, с точки зрения модели оператор AREA- полиморфный, т.е. при разных обращениях к нему он может принимать разные типы аргументов. Особо отметим, что такой полиморфизм является логическим следствием наследования: если есть наследование, то должен быть и полиморфизм, в противном случае не будет и наследования! Сама по себе идея полиморфизма не нова, как вы уже, по-видимому, поняли. Например, в языке SQL есть полиморфные операторы ( = , + , и многие другие); есть такие операторы и в больщинстве других языков программирования. В некоторых языках пользователям даже разрешается вводить собственные полиморфные операторы. Например, в языке PL/I эта возможность предоставляется с помошью GENERIC-функций. Однако наследования как такового ни в одном из данных примеров нет. Все это примеры так называемого перегружаемого полиморфизма. Вид полиморфизма, демонстрируемый с помошью оператора AREA, называют включаемым полиморфизмом на том основании, что связь между, например, окружностями и эллипсами, по сути, равносильна включению множеств [19.3]. По вполне понятным причинам далее в этой главе не уточненный дополнительно термин полиморфизм будет использоваться для включаемого полиморфизма (кроме тех случаев, когда явным образом будет указано противное). Замечание. Приведенные ниже дополнительные разъяснения помогут вам лучше понять различия между перегружаемым и включаемым полиморфизмом. Перегружаемый полиморфизм означает, что сушествует несколько различных операторов с одним и тем же именем (и пользователь должен знать, что эти операторы, хотя и довольно похожие, на самом деле разные и имеют семантические различия). Например, один оператор + служит для сложения целых чисел, другой оператор + - для сложения отношений и т.д. Включаемый полиморфизм означает, что сушествует ровно один оператор, возможно, с несколькими разными неявными реализациями (но пользователю нет необходимости знать, сушествует одна или несколько версий его реализации: для пользователя, повторяем, есть просто один оператор). Полиморфизм в программировании Рассмотрим следуюший пример. Предположим, необходимо написать программу для отображения некоторой диаграммы, составленной из квадратов, окружностей, эллипсов и т.д. Без использования полиморфизма код мог бы быть подобен следующему. FOR EACH X IN DIAGRAM CASE ; WHEN IS SQUARE ( x ) THEN CALL DISPLAY SQUARE ... ; WHEN IS CIRCLE ( X \ THEN CALL DISPLAY CIRCLE ... : END CASE ; (Здесь предполагается, что сушествуют операторы DISPLAY SQUARE, DISPLAY CIRCLE и т.д., которые могут использоваться для проверки принадлежности данного значения определенному типу.) Благодаря полиморфизму данный код становится гораздо проще и существенно короче. FOR EACH X IN DIAGRAM CALL DISPLAY ( X ) ; Пояснения. В этом варианте оператор DISPLAY является полиморфным. Версия его реализации, верно отрабатывающая значения типа Т, обычно определяется непосредственно при объявлении типа Т и на данный момент должна быть известна системе. Во время выполнения, когда системе нужно будет реализовать вызов оператора DISPLAY с аргументом х, ей потребуется установить конкретный тип аргумента х, а затем вызвать ту версию оператора DISPLAY, которая соответствует обнаруженному типу. Этот процесс называют связыванием во время выполнения или поздним связыванием. Иначе говоря, полиморфизм фактически подразумевает, что выражения и операторы CASE, которые в противном случае должны были бы присутствовать в коде пользователя, будут скрыты от него - система самостоятельно выполняет операции CASE, освобождая пользователя от рутинной работы. Перечислим некоторые следствия вышесказанного, в частности в отношении сопровождения программ. Предположим, например, что новый тип TRIANGLE (Треугольник) определен как еше один непосредственный подтип типа POLYGON, и, следовательно, отображаемая диаграмма теперь может содержать еще и треугольники. Если не использовать полиморфизм, то в каждую программу, которая содержит выражения или операторы CASE, подобные приведенному выше, потребуется добавить код следующего вида. WHEN IS TRIANGLE ( х ) THEN CALL DISPLAY TRIANGLE ... ; При использовании полиморфизма никакой модификации исходного кода вообще не потребуется. Исходя из примеров, подобных приведенному выше, полиморфизм иногда образно характеризуют как метод, позволяющий старому коду вызывать новый код , т.е. благодаря ему программа Р фактически сможет вызывать определенную версию определенного оператора, которой даже не существовало на момент написания этой программы Р. Таким образом, здесь мы имеем другой и более важный случай - пример повторного использования кода: одна и та же программа Р может быть пригодна для данных типа Т, которого, повторяем, даже не существовало на момент написания программы Р. Заменимость Как указывалось ранее, понятие заменимости на самом деле является просто тем же понятием полиморфизма, которое рассматривается с несколько иной точки зрения. Мы уже видели, например, что если оператор AREA(e), где е- эллипс, является допустимым, то оператор AREA(c), где с - окружность, также должен быть допустимым. Иными словами, везде, где система подразумевает эллипс, его всегда можно заменить окружностью. Или в общем виде это можно сформулировать так: везде, где системой подразумевается некоторое значение типа Т, его всегда можно заменить значением типа Т, где Т - подтип типа Т. Это утверждение называют принципом заменимости значения. Отметим, в частности, что из данного принципа следует, что если некоторое отношение г имеет атрибут А, объявленный как атрибут типа ELLIPSE, то некоторые значения атрибута А могут иметь тип CIRCLE, а не именно тип ELLIPSE. Точно так, если некоторый тип Т имеет возможное представление, которое включает какой-то компонент С, объявленный относящимся к типу ELLIPSE, то для некоторых значений v типа Т вызов оператора ТНЕ С( V) может возвращать значение типа CIRCLE, а не типа ELLIPSE. И наконец заметим, что, поскольку заменимость - это просто полиморфизм в другом обличье, она также является логическим следствием наследования. Если есть наследование, то должна быть и заменимость; в противном случае не будет и наследования. Связывание во время выполнения относится, конечно, к вопросам реализации, а не к модели Этот вопрос, в отличие от других вопросов реализации, рассматривается здесь дополнительно, только для того, чтобы упростить правильное понимание концепции наследования в целом.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |