Дорогие друзья!
Поздравляю вас с приятным событием: снова открыт свободный доступ к Web-сайту онлайнового журнала Database Programming & Design (www.dbpd.com). За то время, пока я не имел доступа к этому сайту, Кристофер Дейт успел опубликовать три заметки, которые базируются на его (совместно с Хью Дарвеном) прошлогодней книге "Основания объектно-реляционных баз данных: третий манифест". Поскольку в февральском выпуске DBPG Дейт начал новую серию, посвященную наследованию типов, я прежде всего предлагаю вашему вниманию именно февральскую заметку. Постараюсь представить пересказы и двух предыдущих.
Хочу обратить ваше внимание, что подход Дейта и Дарвена интересен, но не бесспорен. Когда я читал их книгу, временами у меня просто чесались руки написать свою статью с критикой некоторых идей. Наверное, я это действительно сделаю, но сначала давайте дадим полностью высказаться Дейту. Кстати, эта первая заметка сама по себе полезна для всех людей, которые еще не составили собственного представления о типах данных. Желаю вам интересного чтения и всяческих успехов.
До скорой встречи, Сергей Кузнецов
Intelligent Enterprise's
Database Programming & Design OnLine
February 1999
According to Date
(http://www.dbpd.com/vault/9902/date9902.html)
Закладывая основу нашего исследования наследования типов
Почему наследование типов?
Предварительные замечания
Что имеется в виду, когда мы говорим "тип"?
Все значения типизированы
Типизированы и все переменные
Простое и множественное наследование
Скаляры, кортежи и отношения
Структурное и поведенческое наследование
Подтаблицы и супертаблицы
Литература
В этом месяце мы начинаем еще одну минисерию статей, которая посвящается понятиям подтипов, супертипов и наследования типов. Как я упоминал в своей заметке в ноябре 1998 г., эта тема оказалась удивительно сложной -- несмотря на тот факт, что основная идея очень проста. (Как это часто случается, черт скрывается в деталях.) Основную идею можно пояснить с помощью примера. Предположим, что имеются два типа ELLIPSE (эллипс) и CIRCLE (окружность) с очевидным смыслом. Тогда можно сказать, что тип CIRCLE является подтипом типа ELLIPSE (эквивалентно, тип ELLIPSE является супертипом типа CIRCLE), что понимается следующим образом:
Таким образом, тип CIRCLE наследует операции и ограничения от типа ELLIPSE (свободно выражаясь), но имеет также собственные операции и ограничения, не применимые к типу ELLIPSE. Замечание: Здесь и во всей этой серии я буду использовать неуточненный термин "ограничение" в конкретном смысле ограничения типа, а в противном случае явно оговаривать другой смысл. Чтобы напомнить, ограничение типа (называемое также ограничением домена) - специфицирует просто допустимые значения данного типа.
Кстати, в этом месте новички иногда впадают в заблуждение: а именно, подтип обладает подмножеством значений, но супермножеством свойств. (Я использую термин "свойства" в качестве удобного сокращения для "операций и ограничений".) Например, тип CIRCLE содержит подмножество значений типа ELLIPSE, но индивидуальная окружность обладает всеми свойствами эллипса, а также и другими.
Вероятно, мне следует начать с обсуждения вопроса "почему эта тема заслуживает первоочередного внимания?". На этот вопрос возможны по крайней мере два ответа:
Во-первых, как кажется, идеи подтипизации и наследования естественно возникают в реальном мире. Т.е. не являются необычными ситуации, в которых все значения данного типа обладают определенными общими свойствами, но некоторые из этих значений имеют собственные дополнительные специальные свойства (примером являются эллипсы и окружности). Тем самым, подтипизация и наследование могут быть полезными средствами для "моделирования действительности".
Во-вторых, если мы осознаем такие характерные свойства подтипизации и наследования и встроим соответствующие возможности и в прикладное и системное программное обеспечение, то сможем достичь некоторой практической экономии. Например, программа, которая работает с эллипсами смогла бы работать и с окружностями, даже если бы сначала она была написана вообще не принимая во внимание окружности (может быть, тип CIRCLE не был определен во время написания программы): так называемое преимущество повторного использования кода.
Однако, несмотря на потенциальные преимущества, все еще ощущается небольшое согласие относительно формальной, строгой и абстрактной модели наследования типов -- хотя существуют языки и продукты, в которых поддерживается некоторая разновидность наследования типов (и уже в течение некоторого времени) и в течение многих лет в книгах, статьях и презентациях обсуждается наследование типов. Цитируя Эндрю Тайвалсаари (Andrew Taivalsaari), "Основная идея наследования весьма проста … [несмотря на] ее центральную роль в современных … системах, наследование все еще представляет собой противоречивый механизм … [Какое-либо] исчерпывающее представление о наследовании все еще отсутствует."
Вот еще несколько цитат, иллюстрирующих тот же самый общий аспект:
Другие люди предлагают модели наследования, которые обладают противоречивыми, неинтуитивными и другими нежелательными свойствами. Например, текущие предложения SQL3 допускают такие вещи, как "неквадратные квадраты" [6] (т.е. значения типа SQUARE, стороны которого имеют разную длину) -- с тем результатом, что SQL3 трудно считать "хорошей моделью реальности". В действительности SQL3 даже не дает возможности устанавливать ограничения типа (вроде того, что у значений типа SQUARE стороны должны иметь одинаковую длину), не позволяет поддерживать их без посторонней помощи -- это, безусловно, неудовлетворительное положение дел. Что еще хуже, SQL3 не дает возможности устанавливать и поддерживать такие ограничения даже при отсутствии поддержки наследования, вероятно, при том предположении, что такая поддержка появится когда-то в будущем.
Однако в нашей книги The Third Manifesto мы с Хью Дарвеном предлагаем модель наследования, которая, как мы полагаем, является хорошей "моделью реальности" и не страдает такими недостатками [7]. Конечно, данная серия основывается на этой модели; по существу, она представляет собой легкое введение (не слишком глубокое) в наиболее важные идеи этой модели. Поэтому позвольте мне немедленно со стыдом сознаться, что одной из моих целей является реклама. Мы хотели бы, чтобы большая индустрия обратила серьезное внимание на наши идеи, потому что мы полагаем, что они могли бы служить основанием общепринятой модели, отсутствие которой так остро ощущается в настоящее время.
Прежде, чем двигаться дальше, мне, вероятно, следует предупредить вас, что приведенные ранее в этом разделе цитаты показывают, что некоторые авторы доказывают существование многих видов наследования, которые разным образом перекрываются, но все отличны друг от друга. Конечно, можно найти в литературе определения терминов "наследование" и "подтип", но они отличаются от наших.
К сожалению, имеется большое число предварительных замечаний, которые мне требуется сделать прежде, чем я смогу приступить к реальному обсуждению наследования как такового, и эти предварительные замечания являются основным предметом данной заметки.
В прошлом я обсуждал некоторые очень важные логические различия: модели и реализации, значений и переменных, доменов (или типов) и отношений, типов и представлений. Оказалось, что все эти различия имеют отношения к теме наследования; в действительности, мы предполагаем, что путаница, наблюдаемая нами в литературе, относящейся к этой области -- а ее много -- является результатом непринятия во внимание этих различий должным образом. Поэтому позвольте мне напомнить некоторые из основных понятий.
Выражаясь нестрого, тип - это именованное множество значений (т.е. все возможные значения данного типа) вместе с ассоциированным набором операций, которые можно применять к этим значениям. Например, тип SMALLINT мог бы состоять из всех целых чисел в диапазоне от -32,768 до +32,767, а ассоциированным набором операций могли бы быть "+", "-", "*", "=", "<" и т.д. В более общей постановке я полагаю, что элементы следующего списка представляют собой аспекты концепции типа:
Заметим, что в реляционном мире типы обычно называются "доменами"; в объектном мире они обычно называются "классами". В этой серии я буду использовать термин "тип" (большей частью).
Можно представлять, что с каждым значением связано что-то вроде флага, на котором написано "я целое", "я номер служащего", "я точка", "я эллипс" или что-то еще. При отсутствии наследования у каждого значения в точности один тип. Однако при наличии наследования значение может одновременно относиться к нескольким типам; например, данное значение в одно и то же время может относиться к типам CIRCLE и ELLIPSE.
Каждая переменная объявляется как переменная в точности одного типа. В языке Tutorial D, например, мы могли бы объявлять переменные следующим образом:
VAR E ELLIPSE ;
Здесь объявленным типом переменной E является ELLIPSE. При отсутствии наследования все возможные значения данной переменной относятся в точности к одному типу, а именно, к соответствующему объявленному типу. Однако при наличии наследования данная переменная может иметь значение, которое одновременно относится к нескольким типам; например, текущее значение переменной E может быть эллипсом, который на самом деле является окружностью, и, следовательно, в одно и то же время относится и к типу ELLIPSE, и к типу CIRCLE.
Имеются две широких "разновидности" наследования типов - одиночное и множественное. Свободно говоря, одиночное наследование означает, что у каждого подтипа имеется только один супертип, и этот подтип наследует свойства только от одного данного супертипа; множественное наследование означает, что у подтипа может иметься произвольное число супертипов, и этот подтип наследует свойства у всех них. Очевидно, что первая разновидность является частным случаем второй. Однако с учебной точки зрения удобно сначала разобраться с одиночным наследованием, оставив на потом сложности множественного наследования, и так я и поступлю в этой серии. До особого замечания я буду использовать неуточненный термин наследование в смысле одиночное наследование.
В приведенном кратком обсуждении типов, значений и переменных я молчаливо предполагал, что говорю о скалярных типах, значениях и переменных. Но, конечно, наследование применимо и к нескалярным значениям -- в частности для значений-кортежей и значений-отношений -- поскольку, в конце концов, такие нескалярные значения строятся на основе скалярных значений. Однако мы не можем даже начать осмысленно говорить о таких вещах, пока не разберемся с тем, что означают подтипизация и наследование в скалярном случае. До особого замечания я буду использовать неуточненные термины "тип", "значение" и "переменная" в смысле скалярных типов, значений и переменных.
Напомним, что скалярные значения могут иметь внутреннюю (физическую) структуру или представление произвольной сложности; например, как мы уже знаем, эллипсы и окружности при соответствующих обстоятельствах могут законным образом рассматриваться как скалярные значения, хотя их внутренняя структура может быть достаточно сложной. Однако эта внутренняя структура всегда скрыта от пользователя. Следовательно, что при обсуждении наследования (по крайней мере, в соответствии с нашей моделью) мы не имеем в виду наследование структуры, поскольку с точки зрения пользователя нет никакой структуры для наследования! Другими словами, нас интересует то, что иногда называют поведенческим наследованием, а не структурное наследование (где под "поведением" понимаются операции -- хотя напоминаю, что наследуются и ограничения). Заметьте, что мы не устраняем структурное наследование; мы всего лишь относимся к нему как к реализационному вопросу, который не существенен для модели.
Рис. 1. Пример подтаблицы/супертаблицы
И еще одно предварительное замечание. Предмет наследования типов в большой степени связан с данными в целом, но не так сильно касается постоянно хранимых (persistent) данных или данных баз данных (т.е. в идее наследования содержится в лучшем случае лишь небольшая часть того, что применимо только к постоянно хранимым данных). Поэтому для простоты все мои примеры в этой серии будут выражаться в терминах локальных данных, а не данных базы данных. (Поддержка наследования действительно имеет определенное влияние на реляционные данные и реляционные операции, но даже это влияние относится только к локальным отношениям вне их связи с базой данных.)
Приношу свои извинения относительно того, что заметка этого месяца носит очень облегченный технический характер. Однако было необходимо сказать все это, прежде чем приступать к глубокому разъяснению наследования. Могу уверить вас, что следующие части будут содержать солидное техническое обсуждение.