В данном обзоре вопросов и решений проблемы сохраняемости объектов мы рассмотрим три подхода, имеющихся в распоряжении ОО-разработчиков. Во-первых, они могут рассчитывать на механизмы поддержки сохраняемости, включенные в язык программирования и окружение разработки, позволяющие помещать и извлекать объекты из некоторого постоянного хранилища.
Во-вторых, они могут объединить объектную технологию с наиболее доступным видом (не ОО) БД - реляционными базами данных. И, в-третьих, можно использовать новые объектно-ориентированные системы баз данных, которые переносят на БД идеи ОО-технологии. Эти методы по очереди описываются в данной лекции, в ней также имеется обзор технологии ОО-баз данных с упором на два наиболее известных продукта. Завершается лекция обсуждением гипотетической будущей судьбы баз данных в ОО-контексте.
Для удовлетворения многих потребностей в сохраняемости достаточно иметь связанный с окружением разработки набор механизмов для записи объектов в файлах и их чтения. Для простых объектов, таких как числа или символы, можно использовать средства ввода-вывода, аналогичные средствам традиционного программирования.
Как только появляются
(рис 13.1) Необходимость в замыкании при сохраненииПринцип loved_one объекта O1.
Мы рассмотрели механизмы класса STORABLE, предоставляющие соответствующие средства: store для сохранения структуры объекта и для ее извлечения. Это ценный механизм, чье присутствие в ОО-окружении само по себе является большим преимуществом перед традиционными окружениями. В лекции 8 курса "Основы объектно-ориентированного программирования" приведен типичный пример его использования: реализация команды редактора SAVE. Вот еще один пример из практики нашей фирмы ISE. Наш компилятор выполняет несколько проходов по представлениям текста программы. Первый проход создает внутреннее представление, называемое Деревом store ; а следующий проход начинается восстановлением .
Механизм STORABLE работает не только на файлах, но и на сетевых соединениях таких, как сокеты; он на самом деле лежит в основе библиотеки клиент-сервер Net.
У процедуры store имеется несколько вариантов. Один, basic_store (базовое_сохранение), сохраняет объекты для их последующего возвращения в ту же систему, работающую на машине той же архитектуры, в процессе того же или последующего ее исполнения. Эти предположения позволяют использовать наиболее компактную форму представления объектов.
Другой вариант, independent_store (независимое_сохранение), обходится без этих предположений; представление объекта в нем не зависит от платформы и от системы. Поэтому оно занимает несколько больше места, так как использует переносимое представление для чисел с плавающей точкой и для других числовых значений, а также должно включать некоторую простую информацию о классах системы. Но он важен для систем типа клиент-сервер, которые должны обмениваться потенциально большими и сложными наборами объектов, находящимися на машинах весьма разных архитектур, работающих в различных системах. Например, сервер на рабочей станции и клиент на PC могут выполнять два разных приложения и взаимодействовать с помощью библиотеки Net. .
Заметим, что только сохранение требует нескольких процедур - basic_store, independent_store. Хотя реализация операции возврата для каждого формата своя, всегда можно использовать один компонент , реализация которого определит формат возвращаемых данных, используемый в файле или сети, и автоматически применит соответствующий алгоритм извлечения.
Принцип
Но в некоторых практических ситуациях требуется немного изменить структуру данных перед тем, как к ней будут применены такие механизмы, как STORABLE или средства ОО-баз данных, рассматриваемые далее в этой лекции. Иначе можно получить больше, чем хотелось бы.
Такая проблема возникает, в частности, из-за разделяемых структур, как в следующем примере.
Требуется заархивировать сравнительно небольшую структуру данных. Так как она содержит одну или более ссылок на большую разделяемую структуру, то принцип address ссылаться на гораздо большее множество объектов, представляющих географическую информацию. Аналогичная ситуация возникает в продукте ArchiText фирмы ISE, позволяющем пользователям манипулировать структурами таких документов, как программы или спецификации. Каждый документ, подобно структуре FAMILY на рис. 13.2, содержит ссылку на структуру, представляющую основную CITY для FAMILY. Мы хотели бы сохранять документ, а не
(рис 13.2) Малая структура, ссылающаяся на большую разделяемую структуруВ таких случаях хочется "оборвать" ссылки на разделяемые структуры перед сохранением ссылающейся структуры. Однако это тонкая процедура. Во-первых, нужно, как всегда, быть уверенным, что в момент новой загрузки объекты останутся совместными - будут удовлетворять своим инвариантам. Но здесь есть и практическая проблема: как обойтись без усложнений и ошибок? На самом деле, не хотелось бы менять исходную структуру, ссылки нужно оборвать только в сохраняемой версии.
И вновь методы построения ОО-ПО дают элегантное решение проблемы, основанное на идеях классов поведения, рассмотренных при обсуждении наследования. Одна из версий процедуры сохранения custom_independent_store работала так же, как и предопределенная процедура independent_store. Но она позволяла также каждому потомку библиотечного класса ACTIONABLE переопределять ряд процедур, которые по умолчанию ничего не делали, например, процедуры pre_store и post_store, выполняющиеся непосредственно перед и после сохранения объекта. Таким образом, можно, чтобы pre_store выполняла:
preserve; address := Void,
где - это тоже компонент ACTIONABLE, который куда-нибудь безопасно копирует объект. Тогда post_action будет выполнять вызов:
restore,
восстанавливающий объект из сохраненной копии.
В общем случае того же эффекта можно добиться с помощью вызова вида:
store_ignore ("address"),
где store_ignore получает в качестве аргумента имя поля. Так как реализация store_ignore может просто пропускать поле, устраняя необходимость двустороннего копирования посредством и , то в данном случае это будет более эффективно, но механизм pre_store-post_store является общим, позволяя выполнять необходимые действия до и после сохранения. Разумеется, нужно убедиться в том, что эти действия не будут неблагоприятно влиять на объекты.
Аналогично можно справиться и с проблемой несовместности во время возвращения, для этого достаточно переопределить процедуру post_retrieve, выполняемую непосредственно перед тем, как восстанавливаемый объект присоединится к сообществу уже одобренных объектов. Например, приложение может переопределить post_retrieve в нужном классе-наследнике ACTIONABLE, чтобы она работала так:
address := my_city_structure.address_value (...)
тем самым снова делая объект представительным, еще до того, как он сможет нарушить инвариант своего класса или какое-нибудь неформальное ограничение.
Конечно, нужно соблюдать некоторые правила, связанные с механизмом класса ACTIONABLE ; в частности, pre_store не должна вносить в структуры данных никаких изменений, которые не были бы сразу же исправлены процедурой post_store. Нужно также обеспечить, чтобы post_retrieve выполняла необходимые действия (часто те же, что и post_store ) для корректировки всех несовместностей, внесенных в сохраненные данные процедурой pre_store. Предложенный механизм, используемый с соблюдением указанных правил, позволит вам остаться верным духу принципа
Этот общий вопрос возникает при всех подходах к ОО-сохраняемости. Классы могут меняться. Что произойдет, если изменится класс, экземпляры которого находятся где-то на постоянном хранении? Этот вопрос называют проблемой эволюции схемы.
Слово схема (schema) пришло из мира реляционных баз данных, где она задает архитектуру БД: множество ее отношений с указанием того, что называется их типами, - число полей и тип каждого поля. В ОО-контексте схема тоже будет множеством типов, определяемых в этом случае классами. |
Хотя некоторые средства разработки и системы баз данных обладают интересными средствами для эволюции ОО-схем, ни одно из них не дает полностью удовлетворительного решения. Давайте определим компоненты полного подхода.
Будет полезно ввести некоторую точную терминологию. Эволюция схемы имеет место, если хотя бы один класс системы, возвращающей объекты ( возвращающая система ), отличается от своего прототипа в системе, сохранившей эти объекты ( сохраняющая система ). Рассогласование при возврате объекта или просто рассогласование объекта имеет место, когда возвращающая система реально возвращает некоторый объект, у которого изменился породивший его класс. Рассогласование объекта - это следствие эволюции схемы одного или нескольких классов, отражающееся на конкретном объекте.
Напомним, несмотря на термины "сохраняющая система" и "возвращающая система" наше обсуждение применимо не только к сохранению и возврату, использующим файлы и БД, но также и к передаче объектов по сети, как в библиотеке Net. В этом случае более аккуратными терминами были бы "посылающая система" и "получающая система". |
Для упрощения обсуждения примем обычное предположение, что программная система не изменяется в процессе ее выполнения. Это означает, что все сохраненные экземпляры класса относятся к одной и той же версии, поэтому во время возвращения либо все они приведут к рассогласованию, либо ни один из них не будет рассогласован. Это предположение не слишком ограничительное, оно не исключает случая баз данных, которые содержат экземпляры многих разных версий одного класса, созданные различными выполнениями системы.
Мы можем исключить два крайних подхода к эволюции схем:
На самом деле нам нужен способ трансформации объектов " на лету " в то время, когда они возвращаются или изменяются. Такое решение является наиболее общим, и далее мы будем рассматривать только его.
Если потребуется механизм одновременной трансформации многих объектов, то механизм "на лету" легко позволит это сделать: достаточно написать маленькую систему, которая возвращает все существующие объекты, используя новые классы, при необходимости применяет трансформацию на лету и все сохраняет. |
Механика преобразования на лету может оказаться весьма мудреной: мы должны быть очень внимательны, чтобы не получить в результате испорченные объекты или БД.
Во-первых, у приложения может не оказаться права изменять запомненный объект из-за существования разных версий породившего его класса. Это вполне разумно, поскольку другие приложения могут все еще использовать старую версию этого объекта. Проблема эта не нова для баз данных. Можно сделать так, чтобы используемый приложением объект был совместим с описанием собственного класса; механизм преобразования на лету обеспечит выполнение этого свойства. Заносить ли преобразованный объект обратно - это отдельный вопрос, классический вопрос привилегированного доступа, возникающий всякий раз, когда несколько приложений или несколько сессий одного и того же приложения получают доступ к сохранению данных. Различные его решения предлагаются базами данных, обычными и ОО.
Независимо от ответа на вопрос о сохранении после изменения более новая и трудная проблема состоит в том, что каждое приложение должно делать с устаревшими объектами. Эволюция схемы включает три отдельных аспекта - выявление, извещение и исправление:
Все три задачи являются весьма тонкими. К счастью, их можно решать по отдельности.
Мы определим две общих категории политики выявления: номинальную (nominal) и структурную (structural).
В обоих случаях задача состоит в выявлении рассогласования между двумя версиями породившего объект класса, существующих в сохраняющей и возвращающей системах.
При номинальном подходе каждая версия класса идентифицируется именем. Это предполагает наличие специального механизма регистрации, который может иметь два варианта:
Для каждого из этих решений требуется некоторый централизованный регистр. Если вы хотите избежать связанных с этим трудностей, то используйте
Что входит в дескриптор класса? Ответ связан с соотношением между эффективностью и надежностью. Из соображений эффективности не хотелось бы тратить много места на информацию о классе или чересчур много времени на сравнение дескрипторов во время возвращения, но надежность требует минимизации риска пропуска рассогласования. Вот несколько возможных стратегий:
C3 - это минимальная разумная политика, и в обычных случаях она представляется хорошим выбором, по крайней мере для начала.
Что должно произойти после того, как номинальный или структурный механизм выявления выловит рассогласование объекта?
Хотелось бы, чтобы возвращающая система узнала об этом и сумела предпринять необходимые корректирующие действия. Этой проблемой будет заниматься некоторый библиотечный механизм. Класс GENERAL (предок всех классов) должен содержать процедуру:
correct_mismatch is
do
...См. полную версию ниже...
end
и правило, что любое выявленное рассогласование объекта приводит к вызову correct_mismatch ( корректировать_рассогласование ) на временно возвратившейся версии объекта. Каждый класс может переопределить стандартную версию correct_mismatch аналогично всякому переопределению процедур создания и стандартной обработки исключений default_rescue. Любое переопределение correct_ должно сохранять инвариант класса.
Что должна делать стандартная (определенная по умолчанию) версия correct_mismatch? Дать ей пустое тело, демонстрируя ненавязчивость, не годится. Это означало бы по умолчанию игнорирование рассогласования, что привело бы к всевозможным ненормальностям в поведении системы. Для глобальной стандартной процедуры лучше возбудить соответствующее исключение:
correct_mismatch is
-- Обработка рассогласования объекта при возврате
do
raise_mismatch_exception
end
где процедура, вызываемая в теле, делает то, что подразумевается ее именем. Это может привести к некоторым неожиданным исключениям, но лучше это, чем разрешить рассогласованиям остаться незамеченными. Если в проекте требуется переделать это предопределенное поведение, например, выполнять пустую инструкцию, а не возбуждать исключение, то всегда можно переопределить correct_mismatch, на свой страх и риск, в классе ANY. (Как вы помните, определенные разработчиками классы наследуют GENERAL не прямо, а через класс ANY, который может быть переделан при проектировании или инсталляции.)
Для большей гибкости имеется также компонент |
Если вы предполагаете, что у объектов некоторого класса возникнут рассогласования, то лучше не рассчитывать на обработку исключений по умолчанию, а переопределить correct_mismatch так, чтобы сразу изменять возвращаемый объект. Это приводит нас к последней задаче - исправлению.
Как следует исправлять объект, для которого при возвращении обнаружено рассогласование? Ответ требует аккуратного анализа и более сложного подхода, чем обычно реализуется в существующих системах или предлагается в литературе.
Ситуация такова: механизм возвращения (с помощью компонента класса STORABLE, соответствующей операции БД или другого доступного примитива) создал в возвращающей системе новый объект, исходя из некоторого сохраненного объекта того же класса, но обнаружил при этом рассогласование. Новый объект в его временном состоянии может быть неправильным, например, он может потерять некоторое поле, присутствовавшее у сохраненного объекта, или приобрести поле, которого не было у оригинала. Рассматривайте его как иностранца без визы.
(рис 13.3) Рассогласование объектаТакое состояние объекта аналогично промежуточному состоянию объекта, создаваемого - вне всяких рассуждений о сохранении - с помощью инструкции создания |
Предположим для простоты, что метод выявления является структурным и основан на атрибутах (т. е. на определенной выше политике C3), хотя приведенное далее обсуждение распространяется и на другие решения, как номинальные, так и структурные. Рассогласование является следствием изменения свойств
Удаление атрибута не вызывает никаких трудностей: если в новом классе отсутствует некоторый атрибут старого класса, то соответствующие поля в объекте больше не нужны и их можно просто убрать. Фактически, процедура correct_mismatch ничего не должна делать с такими полями, поскольку механизм возвращения при создании временного экземпляра нового класса будет их отбрасывать. На рисунке это показано для нижнего поля - скорее, уже не поля - изображенного объекта.
Можно было бы, конечно, проявить больше заботы об отбрасываемых полях. А что, если они были действительно необходимы, а без них объект потеряет свой смысл? В таком случае нужно иметь более продуманную политику выявления, например, такую, как структурная политика C4, которая учитывает инварианты. |
Более тонкая вещь - добавление атрибута в новый класс, приводит к появлению нового поля в возвращаемых объектах. Что делать с таким полем? Нужно его как-то инициализировать. В известных мне системах, поддерживающих эволюцию схемы и преобразование объектов, решение состоит в использовании предопределенных значений, заданных по умолчанию (обычно для чисел выбирается ноль, для строк - пустая строка). Но, как следует из обсуждения похожих проблем, возникающих, например, в контексте наследования, это решение может оказаться очень плохим!
Вспомним стандартный пример - класс ACCOUNT с атрибутами deposits_list и withdrawals_list. Предположим, в новой версии добавлен атрибут balance. Система, используя новую версию, пытается возвратить некоторый экземпляр, созданный в предыдущей версии.
(рис 13.4) Возвращение объекта account (счет).(Подумайте, что не в порядке на этом рисунке?)Цель добавления атрибута balance понятна: вместо того, чтобы перевычислять баланс счета по каждому требованию, мы держим его в объекте и обновляем при необходимости. Инвариант нового класса отражает это с помощью предложения вида:
balance = deposits_listltotal - withdrawals_listltotal
Но, если применить к полю balance возвращаемого объекта инициализацию по умолчанию, то получится совершенно неправильный результат, в котором поле с балансом счета не согласуется с записями вкладов и расходов. На приведенном рисунке balance из-за инициализации по умолчанию нулевой, а в соответствии со списком вкладов и расходов он должен равняться $1000.
Это показывает важность механизма корректировки correct_mismatch. В данном случае можно просто переопределить эту процедуру:
correct_mismatch is
-- Обработать рассогласование объекта, правильно установив balance
do
balance := deposits_list.total -withdrawals_list.total
end
Если автор нового класса ничего не запланирует на этот случай, то предопределенная версия correct_mismatch возбудит исключение, которое аварийно остановит приложение, если не будет обработано (реализующим другую возможность восстановления). Это правильный выход, поскольку продолжение вычисления может нарушить целостность структуры выполняемого объекта и, что еще хуже, структуры сохраненного объекта, например БД. Используя предыдущую метафору, можно сказать, что мы будем отвергать объект до тех пор, пока не сможем присвоить ему надлежащий иммигрантский статус.
Использование класса STORABLE становится недостаточным для приложений, полностью основанных на БД. Его ограниченность отмечалась уже выше: имеется лишь один входной объект, нет поддержки для запросов, основанных на содержимом, каждый вызов заново создает всю структуру, без всякого разделения объектов в промежутках между последовательными вызовами. Кроме того, в STORABLE не поддерживается одновременный доступ разных приложений клиента к одним и тем же сохраненным данным.
Хотя различные расширения этого механизма могут облегчить или устранить некоторые из этих проблем, полностью отработанное решение требует отдать предпочтение технологии баз данных.
Набор механизмов, ОО или нет, предназначенных для сохранения и извлечения элементов данных (в общем случае "объектов") заслуживает названия
Стандартный пример транзакции - это перевод денег в банке с одного счета на другой, требующий двух операций - занесения в дебет первого счета и в |
Приведенный список свойств не является исчерпывающим; он отражает то, что предлагается большинством коммерческих систем и ожидается пользователями.
Безусловно, сегодня наиболее общей формой СУБД является реляционная (relational) модель, базирующаяся на идеях, предложенных Коддом (E. F. Codd) в статье 1970 года.
Реляционная БД - это набор отношений (relations), каждое из которых состоит из множества [records] ). Отношения также называются таблицами, а BOOKS ( КНИГИ ):
| title (название) | date (дата) | pages (страницы) | author (автор) |
|---|---|---|---|
| "The Red and the Black" | 1830 | 341 | "STENDHAL" |
| "The Charterhouse of Parma" | 1839 | 307 | "STENDHAL" |
| "Madame Bovary" | 1856 | 425 | "FLAUBERT" |
| "Euge_nie Grandet" | 1833 | 346 | "BALZAC" |
Каждый title, date и т. д. Имена полей (столбцов) называются атрибутами (attributes).
Реляционные базы обычно являются нормализованными, среди прочего это означает, что каждое поле имеет простое значение (целое число, вещественное число, строка, дата) и не может быть ссылкой на другой
Реляционной модели баз данных сопутствует реляционная алгебра, в которой определено много операций над отношениями. Три типичные операции - это выбор (selection), проекция (projection) и соединение (join).
Выбор выдает отношение, содержащее подмножество строк данного отношения, удовлетворяющее некоторому условию на значения полей. Применяя условие выбора " pages меньше, чем 400" к BOOKS, получим отношение, состоящее из первой, второй и последней строки BOOKS.
Проекция отношения на один или несколько атрибутов получается пропуском всех других полей и устранением повторяющихся строк в получившемся результате. Если спроектировать наше отношение на последний атрибут, то получим отношение с одним полем и с тремя кортежами: " STENDHAL ", " FLAUBERT " и " BALZAC ". Если же спроектировать его на три первых атрибута, то получится отношение с тремя полями, полученное из исходного вычеркиванием последнего столбца.
Соединение двух отношений это комбинированное отношение, полученное путем выбора атрибутов с AUTHORS ( АВТОРЫ ):
| Name (имя) | real_name (настоящее_имя) | Birth (год_ рождения) | death (год_ смерти) |
|---|---|---|---|
| "BALZAC" | "Honore_ de Balzac" | 1799 | 1850 |
| "FLAUBERT" | "Gustave Flaubert" | 1821 | 1880 |
| "PROUST" | "Marcel Proust" | 1871 | 1922 |
| "STENDHAL" | " |
1783 | 1842 |
Тогда
| title | date | pages | author/name | real_name | birth | death |
|---|---|---|---|---|---|---|
| "The Red and the Black" | 1830 | 341 | "STENDHAL" | " |
1783 | 1842 |
| "The Charterhouse of Parma" | 1839 | 307 | "STENDHAL" | " |
1783 | 1842 |
| "Madame Bovary" | 1856 | 425 | "FLAUBERT" | "Gustave Flaubert" | 1821 | 1880 |
| "Euge_nie Grandet" | 1833 | 346 | "BALZAC" | "Honore_ de Balzac" | 1799 | 1850 |
Реляционная модель допускает запросы - одно из главных требований к базе данных в нашем списке - в стандартизированном языке, называемом SQL. Они используются в двух формах: одна применяется непосредственно людьми, а другая ("
select title, date, pages from BOOKS
Он выдает названия, даты и число страниц для всех книг из таблицы BOOKS. Как мы видели, этот запрос в реляционной алгебре является
select title, date, pages, author where pages < 400
соответствует в реляционной алгебре выбору. Запрос:
select
title, date, pages, author, real_name, birth, date
from AUTHORS, BOOKS where
author = name
это внутреннее соединение, дающее тот же результат, что и приведенный пример соединения.
Основные понятия реляционных СУБД, кратко описанные выше, демонстрируют явное сходство с
Многие ОО-окружения предоставляют такую библиотеку для C++, Store ). Этот подход, который можно назвать объектно-реляционным взаимодействием, был успешно испробован во многих разработках. Он подходит в одном из следующих случаев:
Если ваши требования к сохраняемости не подпадают под эти случаи, то вы будете испытывать то, что в литературе называют сопротивлением несогласованности (impedance mismatch) между ОО-
Становление ОО-БД подкреплялось тремя стимулами.
Первый стимул наиболее очевиден для освоивших объектную разработку ПО, когда они сталкиваются с необходимостью сохранения объектов. Но он не обязательно является самым важным. Два других полностью относятся к области баз данных и не зависят от метода разработки.
Изучение понятия ОО-БД начнем с выявления ограничений реляционных систем D2 и того, чем они могут не устроить разработчиков ОО ПО (D1), а затем перейдем к новаторским достижениям движения за ОО-БД.
Было бы абсурдом отрицать вклад систем реляционных БД. (На самом деле в то время как первые публикации по ОО-БД в восьмидесятых склонялись к критике реляционной технологии, современная тенденция состоит в том, чтобы рассматривать эти два подхода как взаимодополняющие.) Реляционные системы являются одним из важнейших компонентов роста информационных технологий, начиная с семидесятых, и будут оставаться им еще долгое время. Они хорошо приспособились к ситуациям, связанным с данными (возможно, больших размеров), в которых:
Типичным примером является БД с данными о налогоплательщиках с большим количеством объектов, представляющих людей, описываемых фиксированными компонентами: ФИО (строка), дата рождения (дата), адрес (строка), зарплата (число) и еще несколько свойств.
Свойство (R3) исключает многие приложения, связанные с мультимедиа,
Как только у нас есть некоторый объект, ссылающийся на другой объект, то ОО-модель обеспечивает простой доступ к непрямым свойствам этого объекта. Например, redblack.author.birth_year возвращает значение 1783, если переменная redblack присоединена к объекту слева на рис. 13.5. Реляционное описание неспособно представить поле со ссылкой author ( автор ), чьим значением является обозначение другого объекта.
(рис 13.5) Объект со ссылкой на другой объектВ реляционной модели имеется обходной путь для этой проблемы, но он тяжелый и непрактичный. Для представления описанной ситуации будут необходимы два отношения BOOKS и AUTHORS, введенные выше. Для их связывания можно выполнить уже показанную операцию соединения, использующую соответствие между полями author первого отношения и name - второго.
Для ответа на вопросы вида: "В каком году родился автор "Красного и черного"?" реляционная реализация должна будет вычислять соединения, проекции и т. п. В данном случае можно использовать указанное выше соединение, а затем взять его проекцию на атрибут birth.
Этот метод работает и широко используется, но он применим только для простых схем. Число операций соединения быстро возрастает в сложных случаях для систем, постоянно обрабатывающих запросы со многими связями, например: "Сколько комнат имеется в предыдущем доме менеджера отдела, из которого дама, закончившая на первом месте среднюю школу вместе с младшим дядей моей жены, была переведена, когда компания-
Простота реляционной модели частично объясняется тем, что объекты однозначно идентифицируются значениями своих атрибутов. Отношение (таблица) является подмножеством A x B x ... некоторых множеств A, B, ...; иными словами, каждый элемент отношения, каждый объект, это a1, b1, ...>, в котором a1 принадлежит A и т. д. Поэтому он не существует вне своего значения, в частности, вставка объекта в отношение не будет иметь никакого эффекта, если в отношении уже имелся идентичный "The Red and the Black", 1830, 341, "STENDHAL" > в приведенное выше отношение BOOKS не приведет к изменению этого отношения. Это сильно отличается от динамичной модели ОО-вычислений, в которой могут существовать два идентичных объекта.
Напомним, что отношение истинно, если obj1 и obj2 - это ссылки, присоединенные к этим объектам, но равенство obj1 = obj2 будет ложным.
Быть идентичными - не значит быть одними и теми же (спросите об этом близнецов). Такая способность различать два этих понятия частично определяет силу моделирования в ОО-технологии. Она основана на понятии идентичности объекта: всякий объект существует независимо от его содержания.
(рис 13.6) Отдельные, но равные (обе нижние ссылки присоединены к одному объекту)Посетителям Императорского дворца в Киото говорят, что эти здания очень древние и каждое перестраивается приблизительно раз в сто лет. С учетом понятия идентичности объекта в этом нет никакого противоречия: объект остается тем же, даже если его содержание меняется. Вы та же личность, что и десять лет назад, хотя ни одной из молекул, составляющих ваше тело в то время, сейчас не осталось. |
Разумеется, в реляционной модели тоже можно выразить идентичность объектов: достаточно добавить к каждому объекту специальное
При создании ОО-ПО, не требующего хранить объекты, поддержка идентичности объекта получается почти случайно: в простейшей реализации каждый объект постоянно хранится по некоторому адресу, а ссылки на объект используют этот адрес, который служит неизменяемым идентификатором данного объекта. (Это неверно для более сложных реализаций, например, фирмы ISE, которые могут перемещать объекты в процессе эффективного сбора мусора; в этих реализациях идентичность объекта является более абстрактным понятием.) Если требуется хранить объекты, то идентичность объекта становится важным фактором ОО-модели.
Поддержка идентичности объектов в разделяемых БД приводит к новым проблемам: каждый клиент, которому нужно создавать объекты, должен получать для них уникальные идентификаторы; это значит, что специальный модуль, ответственный за присвоение идентификаторов, должен быть
Из предыдущих обсуждений можно вывести то, что может быть названо пороговой моделью ОО-БД: минимальное множество свойств, которым должна удовлетворять система БД, чтобы заслужить название ОО-БД (по работе [Zdonik 1990]). (Другие, также весьма желательные, свойства будут обсуждены ниже.) Имеется четыре требования, которым должна удовлетворять пороговая модель: база данных,
Примечательно, что в этом списке отсутствуют некоторые ОО-механизмы, необходимые для этого метода, в частности наследование. Но это не так странно, как может показаться на первый взгляд. Все зависит от того, что мы ожидаем от БД. Система на пороговом уровне должна быть хорошей машиной ОО-БД, предоставляющей набор механизмов для сохранения, возвращения и обхода структур объектов, но оставляющей знания более высокого уровня о семантике этих объектов (например,
Опыт ранних систем ОО-БД подтверждает, что подход |
Имеется много желательных свойств БД, не входящих в пороговую модель. Большинство коммерческих систем предлагают, по крайней мере, некоторые из них.
Первая категория включает непосредственную поддержку более глубоких свойств ОО-метода: наследования (одиночного или множественного),
Так называется способность запоминать предыдущие состояния объекта после, того, как вызовы процедур его изменили. Это особенно важно в случае параллельного доступа. Предположим, что объект O1 содержит ссылку на объект O2. Клиент изменяет некоторые поля O1, отличные от этой ссылки. Другой клиент изменяет O2. Тогда, если первый клиент попытается проследовать по ссылке, он может обнаружить версию O2, несовместную с O1.
Некоторые ОО-СУБД справляются с этой проблемой, трактуя каждую модификацию объекта как создание нового объекта, тем самым, поддерживая доступ к старым версиям объектов.
Объекты - это не единственные элементы, для которых требуется поддержка версий: со временем могут изменяться и порождающие их классы. Это проблема эволюции схемы, которая обсуждалась в начале этой лекции. Только очень немногие ОО-СУБД полностью поддерживают эволюцию схем.
Понятие транзакции уже давно является очень важным для СУБД, но классические механизмы транзакций ориентированы на короткие транзакции, которые начинаются и завершаются одной операцией, выполняемой одним пользователем во время одной сессии работы компьютерной системы. Исходным примером, процитированным в начале этой лекции, служит банковский перевод денег с одного счета на другой; он является транзакцией, поскольку требует либо полного выполнения обеих операций (снятия денег с одного счета и зачисления их на другой), либо (при неудаче) - сохранения исходного состояния. Время, занимаемое этой транзакцией, составляет несколько секунд (даже меньше, если не учитывать взаимодействие с пользователем).
У приложений, связанных с проектированием сложных систем, таких как
Область разработки ПО имеет очевидную потребность в длинных транзакциях, возникающую всякий раз, когда несколько человек или команд работают над общим набором модулей. Интересно, что технология БД не получила широкого распространения (несмотря на многие предложения в литературе) в сфере разработки ПО. Вместо этого, разрабатывались собственные средства
Хотя длинные транзакции концептуально могут и не требовать использования объектной технологии, усилия последнего времени по их поддержке пришли со стороны ОО-СУБД, некоторые из которых предлагают способ проверки любого объекта как в базе данных, так и вне нее.
Каждая СУБД должна предоставлять некоторую форму блокировки объектов для того, чтобы обеспечить безопасный параллельный доступ и обновление. Ранние ОО-СУБД поддерживали блокировку на уровне страниц, при которой границы блокируемой области определялись операционной системой. Это было неудобно как для больших объектов (которые могут занимать несколько страниц), так и для маленьких (которых может быть много на одной странице, так что блокировка одного из них повлечет блокировку и остальных). Новые системы предлагают блокировку на уровне объекта, позволяя приложению клиента блокировать объекты индивидуально.
Последние усилия направлены на минимизацию размера блокировки в процессе реального выполнения, поскольку блокировка может вызвать конфликты и замедление выполнения операций БД. Оптимистическая блокировка - это общее название целого класса методов, которые пытаются устранить априорное навешивание замка на объект, выполняя вместо этого спорные операции на копии объекта, откладывая насколько возможно обновление главной копии, затем блокируют ее, согласовывая конфликтующие обновления. Мы увидим далее пример оптимистической блокировки в системе Matisse.
Как было подчеркнуто выше, СУБД поддерживают запросы. Здесь ОО-системы в случае эволюции схем могут оказаться более гибкими, чем реляционные. Изменение схемы реляционной БД часто означает необходимость изменения текстов запросов и их перекомпиляцию. В ОО-БД запросы формулируются относительно объектов; вы спрашиваете о некоторых компонентах экземпляров некоторого класса. Здесь в качестве экземпляров могут выступать как прямые экземпляры данного класса, так и экземпляры его собственных потомков. Поэтому, если у класса, для которого сформулирован запрос, появляется новый потомок, то данный запрос применим и к экземплярам этого нового класса и позволяет извлекать из них требуемую информацию.
Начиная с середины восьмидесятых появилось большое число продуктов с ОО-СУБД. Некоторыми из наиболее известных являются: Gemstone, Itasca, Matisse, Objectivity, ObjectStore, Ontos, O2, Poet, Versant. Недавно несколько компаний, таких как UniSQL, разработали объектно-реляционные системы, пытаясь объединить наилучшие черты обоих подходов. Главные производители реляционных СУБД также предлагают или анонсируют комбинированные решения, такие как Illustra фирмы
Чтобы облегчить возможность взаимодействия, многие производители ОО-СУБД объединили свои силы в Object
Давайте взглянем на две особенно интересные системы: Matisse и Versant.
MATISSE от фирмы
Matisse - это смелая разработка со многими необычными идеями. Она ориентирована на большие базы данных с богатой семантической структурой и может манипулировать с очень большими объектами, такими как изображения, фильмы и звуки. Хотя она поддерживает основные ОО-понятия, в частности,
Matisse использует оригинальный подход к проблеме минимизации блокировок. Многие системы применяют следующее правило взаимного исключения: несколько клиентов могут читать объект одновременно, но как только один из клиентов начинает писать, ни один из других не может читать или писать. Причина, объясненная в лекции о параллельности, состоит в сохранении целостности объекта, выраженной инвариантами класса. Если разрешить одновременную запись двум клиентам, то объект может стать несовместным, а если некоторый клиент находится в середине процесса записи, то объект может оказаться в нестабильном состоянии (не удовлетворяющем инварианту), так что другой клиент, который его в этот момент читает, получит неверный результат.
Очевидно, что блокировка вида писатель-писатель необходима. Что касается исключений вида писатель-читатель, то некоторые системы их не придерживаются, разрешая операции чтения даже при наличии блокировки записи. Такие операции уместно назвать
Matisse, чьи создатели явно преследовали цель минимизации блокировок, предложил радикальное решение этого вопроса, основанное на организации управления объектами: никаких операций записи. Вместо изменения существующего объекта операция записи (из ПО клиента) создает новый объект. В результате можно читать объекты без всяких блокировок: у вас всегда будет доступ к некоторой версии БД, на которую не повлияли операции записи, которые могли произойти после начала вашего чтения. Вы также можете получить доступ к большому числу объектов с гарантией, что все они принадлежат одной и той же версии БД, в то время как при традиционном подходе для достижения того же результата потребовалось бы использовать глобальные блокировки или транзакции, что привело бы к большой потере эффективности.
Следствием такой политики является возможность возврата к предыдущим версиям объекта или самой БД. По умолчанию, старые версии сохраняются, но система предоставляет "сборщик версий", позволяющий избавляться от нежелательных версий.
Система Matisse предоставляет интересные возможности для работы с отношениями. Например, если у класса EMPLOYEE ( СЛУЖАЩИЙ ) имеется атрибут ( руководитель ): MANAGER, то Matisse (по требованию разработчика) автоматически отслеживает обратные связи, так что можно получить доступ не только к руководителю служащего, но также и ко всем служащим, подчиненным данному руководителю. Кроме того, возможны запросы, ищущие объекты по ключевым словам.
Versant от фирмы Versant
Versant - это одна из СУБД, отвечающих стандарту
При разработке Versant особое внимание было уделено блокировке и транзакциям. В ней можно блокировать отдельные объекты. Приложение может запросить чтение, обновление или запись заблокированного объекта. Обновления служат для устранения взаимных блокировок: если вам нужно прочесть заблокированный объект и записать в него, то требуется вначале запросить право на его обновление, предоставляемое при условии, что никакой другой клиент им в данный момент не пользуется. При этом остальные клиенты могут читать объект, пока не начнется (гарантированное) выполнение вашего запроса на запись. Непосредственный переход от чтения заблокированного объекта к записи мог бы привести к взаимной блокировке: каждый из двух клиентов мог бы ждать до бесконечности, пока другой не снимет свою блокировку.
Механизм транзакций обеспечивает как короткие, так и длинные транзакции; приложение может оставить объект на любое время. Поддерживаются версии объектов и
Другой особенностью Versant является ее механизм, позволяющий сообщать приложению о различных событиях в БД, например, об удалении или обновлении объекта. Получив такое извещение, приложение может выполнить предусмотренные на этот случай действия.
СУБД Versant предоставляет пользователям богатый набор типов данных, включая и множество заранее определенных коллекций классов. Это позволяет проводить эволюцию схемы при условии, что новые поля инициализируются предопределенными значениями. В ней также имеются возможности
Завершим этот обзор мечтами о возможных направлениях развития сохраняемости в будущем. Следующие далее наблюдения носят предварительный характер, их цель - побудить дальнейшие размышления, а не предложить конкретные ответы.
Понятие базы данных произошло от взгляда на мир, в центре которого сидят Данные, а расположенным вокруг программам разрешены доступ и модификация этих Данных:
(рис 13.7) Взгляд со стороны баз данныхОднако в объектной технологии мы научились понимать данные как сущности, полностью определяемые применяемыми к ним операциями:
(рис 13.8) ОО-взглядЭти два взгляда кажутся несовместимыми! Понятие данных, существующих независимо от обрабатывающих их программ ("независимость данных", догмат, повторяемый на первых страницах любой книги по БД) является проклятием для ОО-разработчика. Должны ли мы считать выражение "ОО-база данных"
Возможно, нет, но, быть может, стоит понять, как в догматическом ОО-контексте можно получить эффект баз данных, реально не имея их. Если мы дадим определение (упрощая до самого существенного данное ранее в этой лекции определение БД)
БАЗА ДАННЫХ = СОХРАНЯЕМОСТЬ + РАЗДЕЛЕНИЕ ДАННЫХ,
то догматический взгляд будет рассматривать второй компонент, разделение данных, как несовместимый с ОО-идеями, и сосредоточится только на сохраняемости. Но тогда можно подойти к разделению данных, используя другой метод - параллелизм! Это показано на следующем рисунке.
(рис 13.9) Отделение сохраняемости от разделения данныхСледуя ОО-принципам, сохраняемые данные реализуются как множество объектов - экземпляров некоторых абстрактных типов данных - и управляются некоторой серверной системой. Системы клиентов, которым требуется работать с данными, будут делать это через сервер. Так как эта схема требует разделения и параллельного доступа, то клиенты будут рассматривать сервер как сепаратный в смысле, определенном при обсуждении параллельности в лекции 12. Например:
flights: separate FLIGHT_DATABASE; ...
flight_details (f: separate FLIGHT_DATABASE;
rf: REQUESTED_FLIGHTS): FLIGHT is
do
Result := f.flight_details (rf)
end
reserve (f: separate FLIGHT_DATABASE; r: RESERVATION) is
do
f.reserve (r); status := f.status
end
Тогда на стороне сервера не требуется никакого механизма разделения, а только общий механизм сохранения. Нам могут также понадобиться средства и методы для поддержки версий объектов, но они, по существу, относятся к вопросам сохраняемости объектов, а не к базам данных.
В этом случае механизм сохранения может стать чрезвычайно простым, отбросив многое из багажа БД. Можно даже считать, что все объекты по умолчанию являются постоянно хранимыми, а временные объекты становятся исключением, обрабатываемым механизмом, обобщающим сбор мусора. Такой подход, который невозможно было представить при изобретении БД, становится менее абсурдным при постоянном уменьшении стоимости памяти и росте доступности 64-битовых виртуальных адресных пространств, в которых, как было уже замечено в [Sombrero-Web], " можно создавать каждую секунду новый 4-гигабайтный объект (вся память обычного 32-битного процессора) в течение 136 лет и все еще не исчерпать доступные адреса. Этого достаточно, чтобы сохранить все данные, связанные с почти любым приложением на протяжении всего его существования ".
Все это весьма умозрительно и нет никаких оснований для того, чтобы отказываться от традиционного понятия БД. Не следует также немедленно мчаться и продавать акции компаний, создающих ОО-БД. Рассматривайте это обсуждение как интеллектуальное упражнение: приглашение прозондировать будущее общепринятого понятия ОО-БД, проверив, может ли существующий подход по-настоящему успешно справиться со страшным сопротивлением несогласованности между методом разработки ПО и механизмами, поддерживающими накопление и хранение данных.
Последнее замечание о БД. Взрывной рост Интернета и появление средств поиска, основанного на контексте (в момент написания книги наиболее известными примерами таких средств были AltaVista, Web
СУБД требуют, чтобы перед сохранением любых данных вы сначала конвертировали их в строго определенный формат схемы БД. Недавние исследования, тем не менее, показали, что 80% электронных данных в компаниях являются неструктурированными (т. е. располагаются вне БД, как правило, в текстовых файлах), несмотря на многолетнее
Пока еще ответ - да. Неструктурированные и структурированные данные будут сосуществовать. Но БД больше не являются единственной выбором; все более и более изощренные средства для запросов смогут извлекать информацию, даже если она не имеет формата, требуемого БД. Разумеется, для создания таких средств лучше всего подходит ОО-технология.
Первой работой о реляционной модели была статья [Codd 1970]; имеется множество книг по этой тематике. По-видимому, наиболее известный учебник по БД c упором на реляционную модель - книга [Date 1995] (это шестое издание книги, впервые опубликованной в середине семидесятых). Другой полезный текст общего назначения - [Elmasri 1989].
[Walden 1995] содержит детальное практическое обсуждение того, как выполнять работу по объектно-реляционному взаимодействию. [Khoshafian 1986] выдвигает вопрос об идентификации объектов на передний план обсуждения ОО-БД.
Хорошей отправной точкой для понимания целей ОО-БД и чтения некоторых оригинальных статей является сборник [Zdonik 1990], включающий работы некоторых пионеров этой области, а его вводная лекция явилась источником понятия "пороговой модели", использованного в этой лекции. Широко циркулирующий "Манифест ОО-систем баз данных" [Atkinson 1989], являющийся результатом сотрудничества большого числа экспертов, существенно повлиял на цели движения ОО-БД. Сейчас имеется много учебников по этой тематике, перечислим некоторые из наиболее известных (в порядке публикации): [Kim 1990], [Bertino 1993], [Khoshafian 1993], [Kemper 1994], [Loomis 1995]. Дальнейшие постоянно обновляемые ссылки можно найти в он-лайн библиографии Майкла Лея (Michael Ley) по системам БД [Ley-Web]. Группа Клауса Дитриха (Klaus Dittrich) в Цюрихском университете ведет список часто задаваемых вопросов о ОО-БД http://www.ifi.unizh.ch/groups/dbtg/ObjectDB/ ODBminiFAQ.html. В [Cattel 1993] описан стандарт
Эта лекция очень выиграла от важных комментариев Ричарда Биляка ( Richard Bielak), особенно об эволюции схем, Замыкании Сохраняемости, запросам к ОО-БД, Versant и Sombrero. Описание Versant базируется на [Versant 1994], а Matisse - на [
Предварительный вариант материала этой лекции, посвященного эволюции схем, появился в [M 1996c]. Обсуждение вопроса о том, как соответствуют друг другу ОО-понятия и понятия БД, пришло из неопубликованных заметок лекций, прочитанных в 1995 г. на конференциях TOOLS USA и European Software Engineering Conference [M 1996d].
Предложите, как расширить методы эволюции схем, развитые в этой лекции, чтобы учесть случай, при котором классы программной системы могут изменяться во время выполнения системы.
Обсудите, в каком виде могут формулироваться запросы в ОО-БД.
В данном обзоре вопросов и решений проблемы сохраняемости объектов мы рассмотрим три подхода, имеющихся в распоряжении ОО-разработчиков. Во-первых, они могут рассчитывать на механизмы поддержки сохраняемости, включенные в язык программирования и окружение разработки, позволяющие помещать и извлекать объекты из некоторого постоянного хранилища.
Во-вторых, они могут объединить объектную технологию с наиболее доступным видом (не ОО) БД - реляционными базами данных. И, в-третьих, можно использовать новые объектно-ориентированные системы баз данных, которые переносят на БД идеи ОО-технологии. Эти методы по очереди описываются в данной лекции, в ней также имеется обзор технологии ОО-баз данных с упором на два наиболее известных продукта. Завершается лекция обсуждением гипотетической будущей судьбы баз данных в ОО-контексте.
Для удовлетворения многих потребностей в сохраняемости достаточно иметь связанный с окружением разработки набор механизмов для записи объектов в файлах и их чтения. Для простых объектов, таких как числа или символы, можно использовать средства ввода-вывода, аналогичные средствам традиционного программирования.
Как только появляются
(рис 13.1) Необходимость в замыкании при сохраненииПринцип loved_one объекта O1.
Мы рассмотрели механизмы класса STORABLE, предоставляющие соответствующие средства: store для сохранения структуры объекта и для ее извлечения. Это ценный механизм, чье присутствие в ОО-окружении само по себе является большим преимуществом перед традиционными окружениями. В лекции 8 курса "Основы объектно-ориентированного программирования" приведен типичный пример его использования: реализация команды редактора SAVE. Вот еще один пример из практики нашей фирмы ISE. Наш компилятор выполняет несколько проходов по представлениям текста программы. Первый проход создает внутреннее представление, называемое Деревом store ; а следующий проход начинается восстановлением .
Механизм STORABLE работает не только на файлах, но и на сетевых соединениях таких, как сокеты; он на самом деле лежит в основе библиотеки клиент-сервер Net.
У процедуры store имеется несколько вариантов. Один, basic_store (базовое_сохранение), сохраняет объекты для их последующего возвращения в ту же систему, работающую на машине той же архитектуры, в процессе того же или последующего ее исполнения. Эти предположения позволяют использовать наиболее компактную форму представления объектов.
Другой вариант, independent_store (независимое_сохранение), обходится без этих предположений; представление объекта в нем не зависит от платформы и от системы. Поэтому оно занимает несколько больше места, так как использует переносимое представление для чисел с плавающей точкой и для других числовых значений, а также должно включать некоторую простую информацию о классах системы. Но он важен для систем типа клиент-сервер, которые должны обмениваться потенциально большими и сложными наборами объектов, находящимися на машинах весьма разных архитектур, работающих в различных системах. Например, сервер на рабочей станции и клиент на PC могут выполнять два разных приложения и взаимодействовать с помощью библиотеки Net. .
Заметим, что только сохранение требует нескольких процедур - basic_store, independent_store. Хотя реализация операции возврата для каждого формата своя, всегда можно использовать один компонент , реализация которого определит формат возвращаемых данных, используемый в файле или сети, и автоматически применит соответствующий алгоритм извлечения.
Принцип
Но в некоторых практических ситуациях требуется немного изменить структуру данных перед тем, как к ней будут применены такие механизмы, как STORABLE или средства ОО-баз данных, рассматриваемые далее в этой лекции. Иначе можно получить больше, чем хотелось бы.
Такая проблема возникает, в частности, из-за разделяемых структур, как в следующем примере.
Требуется заархивировать сравнительно небольшую структуру данных. Так как она содержит одну или более ссылок на большую разделяемую структуру, то принцип address ссылаться на гораздо большее множество объектов, представляющих географическую информацию. Аналогичная ситуация возникает в продукте ArchiText фирмы ISE, позволяющем пользователям манипулировать структурами таких документов, как программы или спецификации. Каждый документ, подобно структуре FAMILY на рис. 13.2, содержит ссылку на структуру, представляющую основную CITY для FAMILY. Мы хотели бы сохранять документ, а не
(рис 13.2) Малая структура, ссылающаяся на большую разделяемую структуруВ таких случаях хочется "оборвать" ссылки на разделяемые структуры перед сохранением ссылающейся структуры. Однако это тонкая процедура. Во-первых, нужно, как всегда, быть уверенным, что в момент новой загрузки объекты останутся совместными - будут удовлетворять своим инвариантам. Но здесь есть и практическая проблема: как обойтись без усложнений и ошибок? На самом деле, не хотелось бы менять исходную структуру, ссылки нужно оборвать только в сохраняемой версии.
И вновь методы построения ОО-ПО дают элегантное решение проблемы, основанное на идеях классов поведения, рассмотренных при обсуждении наследования. Одна из версий процедуры сохранения custom_independent_store работала так же, как и предопределенная процедура independent_store. Но она позволяла также каждому потомку библиотечного класса ACTIONABLE переопределять ряд процедур, которые по умолчанию ничего не делали, например, процедуры pre_store и post_store, выполняющиеся непосредственно перед и после сохранения объекта. Таким образом, можно, чтобы pre_store выполняла:
preserve; address := Void,
где - это тоже компонент ACTIONABLE, который куда-нибудь безопасно копирует объект. Тогда post_action будет выполнять вызов:
restore,
восстанавливающий объект из сохраненной копии.
В общем случае того же эффекта можно добиться с помощью вызова вида:
store_ignore ("address"),
где store_ignore получает в качестве аргумента имя поля. Так как реализация store_ignore может просто пропускать поле, устраняя необходимость двустороннего копирования посредством и , то в данном случае это будет более эффективно, но механизм pre_store-post_store является общим, позволяя выполнять необходимые действия до и после сохранения. Разумеется, нужно убедиться в том, что эти действия не будут неблагоприятно влиять на объекты.
Аналогично можно справиться и с проблемой несовместности во время возвращения, для этого достаточно переопределить процедуру post_retrieve, выполняемую непосредственно перед тем, как восстанавливаемый объект присоединится к сообществу уже одобренных объектов. Например, приложение может переопределить post_retrieve в нужном классе-наследнике ACTIONABLE, чтобы она работала так:
address := my_city_structure.address_value (...)
тем самым снова делая объект представительным, еще до того, как он сможет нарушить инвариант своего класса или какое-нибудь неформальное ограничение.
Конечно, нужно соблюдать некоторые правила, связанные с механизмом класса ACTIONABLE ; в частности, pre_store не должна вносить в структуры данных никаких изменений, которые не были бы сразу же исправлены процедурой post_store. Нужно также обеспечить, чтобы post_retrieve выполняла необходимые действия (часто те же, что и post_store ) для корректировки всех несовместностей, внесенных в сохраненные данные процедурой pre_store. Предложенный механизм, используемый с соблюдением указанных правил, позволит вам остаться верным духу принципа
Этот общий вопрос возникает при всех подходах к ОО-сохраняемости. Классы могут меняться. Что произойдет, если изменится класс, экземпляры которого находятся где-то на постоянном хранении? Этот вопрос называют проблемой эволюции схемы.
Слово схема (schema) пришло из мира реляционных баз данных, где она задает архитектуру БД: множество ее отношений с указанием того, что называется их типами, - число полей и тип каждого поля. В ОО-контексте схема тоже будет множеством типов, определяемых в этом случае классами. |
Хотя некоторые средства разработки и системы баз данных обладают интересными средствами для эволюции ОО-схем, ни одно из них не дает полностью удовлетворительного решения. Давайте определим компоненты полного подхода.
Будет полезно ввести некоторую точную терминологию. Эволюция схемы имеет место, если хотя бы один класс системы, возвращающей объекты ( возвращающая система ), отличается от своего прототипа в системе, сохранившей эти объекты ( сохраняющая система ). Рассогласование при возврате объекта или просто рассогласование объекта имеет место, когда возвращающая система реально возвращает некоторый объект, у которого изменился породивший его класс. Рассогласование объекта - это следствие эволюции схемы одного или нескольких классов, отражающееся на конкретном объекте.
Напомним, несмотря на термины "сохраняющая система" и "возвращающая система" наше обсуждение применимо не только к сохранению и возврату, использующим файлы и БД, но также и к передаче объектов по сети, как в библиотеке Net. В этом случае более аккуратными терминами были бы "посылающая система" и "получающая система". |
Для упрощения обсуждения примем обычное предположение, что программная система не изменяется в процессе ее выполнения. Это означает, что все сохраненные экземпляры класса относятся к одной и той же версии, поэтому во время возвращения либо все они приведут к рассогласованию, либо ни один из них не будет рассогласован. Это предположение не слишком ограничительное, оно не исключает случая баз данных, которые содержат экземпляры многих разных версий одного класса, созданные различными выполнениями системы.
Мы можем исключить два крайних подхода к эволюции схем:
На самом деле нам нужен способ трансформации объектов " на лету " в то время, когда они возвращаются или изменяются. Такое решение является наиболее общим, и далее мы будем рассматривать только его.
Если потребуется механизм одновременной трансформации многих объектов, то механизм "на лету" легко позволит это сделать: достаточно написать маленькую систему, которая возвращает все существующие объекты, используя новые классы, при необходимости применяет трансформацию на лету и все сохраняет. |
Механика преобразования на лету может оказаться весьма мудреной: мы должны быть очень внимательны, чтобы не получить в результате испорченные объекты или БД.
Во-первых, у приложения может не оказаться права изменять запомненный объект из-за существования разных версий породившего его класса. Это вполне разумно, поскольку другие приложения могут все еще использовать старую версию этого объекта. Проблема эта не нова для баз данных. Можно сделать так, чтобы используемый приложением объект был совместим с описанием собственного класса; механизм преобразования на лету обеспечит выполнение этого свойства. Заносить ли преобразованный объект обратно - это отдельный вопрос, классический вопрос привилегированного доступа, возникающий всякий раз, когда несколько приложений или несколько сессий одного и того же приложения получают доступ к сохранению данных. Различные его решения предлагаются базами данных, обычными и ОО.
Независимо от ответа на вопрос о сохранении после изменения более новая и трудная проблема состоит в том, что каждое приложение должно делать с устаревшими объектами. Эволюция схемы включает три отдельных аспекта - выявление, извещение и исправление:
Все три задачи являются весьма тонкими. К счастью, их можно решать по отдельности.
Мы определим две общих категории политики выявления: номинальную (nominal) и структурную (structural).
В обоих случаях задача состоит в выявлении рассогласования между двумя версиями породившего объект класса, существующих в сохраняющей и возвращающей системах.
При номинальном подходе каждая версия класса идентифицируется именем. Это предполагает наличие специального механизма регистрации, который может иметь два варианта:
Для каждого из этих решений требуется некоторый централизованный регистр. Если вы хотите избежать связанных с этим трудностей, то используйте
Что входит в дескриптор класса? Ответ связан с соотношением между эффективностью и надежностью. Из соображений эффективности не хотелось бы тратить много места на информацию о классе или чересчур много времени на сравнение дескрипторов во время возвращения, но надежность требует минимизации риска пропуска рассогласования. Вот несколько возможных стратегий:
C3 - это минимальная разумная политика, и в обычных случаях она представляется хорошим выбором, по крайней мере для начала.
Что должно произойти после того, как номинальный или структурный механизм выявления выловит рассогласование объекта?
Хотелось бы, чтобы возвращающая система узнала об этом и сумела предпринять необходимые корректирующие действия. Этой проблемой будет заниматься некоторый библиотечный механизм. Класс GENERAL (предок всех классов) должен содержать процедуру:
correct_mismatch is
do
...См. полную версию ниже...
end
и правило, что любое выявленное рассогласование объекта приводит к вызову correct_mismatch ( корректировать_рассогласование ) на временно возвратившейся версии объекта. Каждый класс может переопределить стандартную версию correct_mismatch аналогично всякому переопределению процедур создания и стандартной обработки исключений default_rescue. Любое переопределение correct_ должно сохранять инвариант класса.
Что должна делать стандартная (определенная по умолчанию) версия correct_mismatch? Дать ей пустое тело, демонстрируя ненавязчивость, не годится. Это означало бы по умолчанию игнорирование рассогласования, что привело бы к всевозможным ненормальностям в поведении системы. Для глобальной стандартной процедуры лучше возбудить соответствующее исключение:
correct_mismatch is
-- Обработка рассогласования объекта при возврате
do
raise_mismatch_exception
end
где процедура, вызываемая в теле, делает то, что подразумевается ее именем. Это может привести к некоторым неожиданным исключениям, но лучше это, чем разрешить рассогласованиям остаться незамеченными. Если в проекте требуется переделать это предопределенное поведение, например, выполнять пустую инструкцию, а не возбуждать исключение, то всегда можно переопределить correct_mismatch, на свой страх и риск, в классе ANY. (Как вы помните, определенные разработчиками классы наследуют GENERAL не прямо, а через класс ANY, который может быть переделан при проектировании или инсталляции.)
Для большей гибкости имеется также компонент |
Если вы предполагаете, что у объектов некоторого класса возникнут рассогласования, то лучше не рассчитывать на обработку исключений по умолчанию, а переопределить correct_mismatch так, чтобы сразу изменять возвращаемый объект. Это приводит нас к последней задаче - исправлению.
Как следует исправлять объект, для которого при возвращении обнаружено рассогласование? Ответ требует аккуратного анализа и более сложного подхода, чем обычно реализуется в существующих системах или предлагается в литературе.
Ситуация такова: механизм возвращения (с помощью компонента класса STORABLE, соответствующей операции БД или другого доступного примитива) создал в возвращающей системе новый объект, исходя из некоторого сохраненного объекта того же класса, но обнаружил при этом рассогласование. Новый объект в его временном состоянии может быть неправильным, например, он может потерять некоторое поле, присутствовавшее у сохраненного объекта, или приобрести поле, которого не было у оригинала. Рассматривайте его как иностранца без визы.
(рис 13.3) Рассогласование объектаТакое состояние объекта аналогично промежуточному состоянию объекта, создаваемого - вне всяких рассуждений о сохранении - с помощью инструкции создания |
Предположим для простоты, что метод выявления является структурным и основан на атрибутах (т. е. на определенной выше политике C3), хотя приведенное далее обсуждение распространяется и на другие решения, как номинальные, так и структурные. Рассогласование является следствием изменения свойств
Удаление атрибута не вызывает никаких трудностей: если в новом классе отсутствует некоторый атрибут старого класса, то соответствующие поля в объекте больше не нужны и их можно просто убрать. Фактически, процедура correct_mismatch ничего не должна делать с такими полями, поскольку механизм возвращения при создании временного экземпляра нового класса будет их отбрасывать. На рисунке это показано для нижнего поля - скорее, уже не поля - изображенного объекта.
Можно было бы, конечно, проявить больше заботы об отбрасываемых полях. А что, если они были действительно необходимы, а без них объект потеряет свой смысл? В таком случае нужно иметь более продуманную политику выявления, например, такую, как структурная политика C4, которая учитывает инварианты. |
Более тонкая вещь - добавление атрибута в новый класс, приводит к появлению нового поля в возвращаемых объектах. Что делать с таким полем? Нужно его как-то инициализировать. В известных мне системах, поддерживающих эволюцию схемы и преобразование объектов, решение состоит в использовании предопределенных значений, заданных по умолчанию (обычно для чисел выбирается ноль, для строк - пустая строка). Но, как следует из обсуждения похожих проблем, возникающих, например, в контексте наследования, это решение может оказаться очень плохим!
Вспомним стандартный пример - класс ACCOUNT с атрибутами deposits_list и withdrawals_list. Предположим, в новой версии добавлен атрибут balance. Система, используя новую версию, пытается возвратить некоторый экземпляр, созданный в предыдущей версии.
(рис 13.4) Возвращение объекта account (счет).(Подумайте, что не в порядке на этом рисунке?)Цель добавления атрибута balance понятна: вместо того, чтобы перевычислять баланс счета по каждому требованию, мы держим его в объекте и обновляем при необходимости. Инвариант нового класса отражает это с помощью предложения вида:
balance = deposits_listltotal - withdrawals_listltotal
Но, если применить к полю balance возвращаемого объекта инициализацию по умолчанию, то получится совершенно неправильный результат, в котором поле с балансом счета не согласуется с записями вкладов и расходов. На приведенном рисунке balance из-за инициализации по умолчанию нулевой, а в соответствии со списком вкладов и расходов он должен равняться $1000.
Это показывает важность механизма корректировки correct_mismatch. В данном случае можно просто переопределить эту процедуру:
correct_mismatch is
-- Обработать рассогласование объекта, правильно установив balance
do
balance := deposits_list.total -withdrawals_list.total
end
Если автор нового класса ничего не запланирует на этот случай, то предопределенная версия correct_mismatch возбудит исключение, которое аварийно остановит приложение, если не будет обработано (реализующим другую возможность восстановления). Это правильный выход, поскольку продолжение вычисления может нарушить целостность структуры выполняемого объекта и, что еще хуже, структуры сохраненного объекта, например БД. Используя предыдущую метафору, можно сказать, что мы будем отвергать объект до тех пор, пока не сможем присвоить ему надлежащий иммигрантский статус.
Использование класса STORABLE становится недостаточным для приложений, полностью основанных на БД. Его ограниченность отмечалась уже выше: имеется лишь один входной объект, нет поддержки для запросов, основанных на содержимом, каждый вызов заново создает всю структуру, без всякого разделения объектов в промежутках между последовательными вызовами. Кроме того, в STORABLE не поддерживается одновременный доступ разных приложений клиента к одним и тем же сохраненным данным.
Хотя различные расширения этого механизма могут облегчить или устранить некоторые из этих проблем, полностью отработанное решение требует отдать предпочтение технологии баз данных.
Набор механизмов, ОО или нет, предназначенных для сохранения и извлечения элементов данных (в общем случае "объектов") заслуживает названия
Стандартный пример транзакции - это перевод денег в банке с одного счета на другой, требующий двух операций - занесения в дебет первого счета и в |
Приведенный список свойств не является исчерпывающим; он отражает то, что предлагается большинством коммерческих систем и ожидается пользователями.
Безусловно, сегодня наиболее общей формой СУБД является реляционная (relational) модель, базирующаяся на идеях, предложенных Коддом (E. F. Codd) в статье 1970 года.
Реляционная БД - это набор отношений (relations), каждое из которых состоит из множества [records] ). Отношения также называются таблицами, а BOOKS ( КНИГИ ):
| title (название) | date (дата) | pages (страницы) | author (автор) |
|---|---|---|---|
| "The Red and the Black" | 1830 | 341 | "STENDHAL" |
| "The Charterhouse of Parma" | 1839 | 307 | "STENDHAL" |
| "Madame Bovary" | 1856 | 425 | "FLAUBERT" |
| "Euge_nie Grandet" | 1833 | 346 | "BALZAC" |
Каждый title, date и т. д. Имена полей (столбцов) называются атрибутами (attributes).
Реляционные базы обычно являются нормализованными, среди прочего это означает, что каждое поле имеет простое значение (целое число, вещественное число, строка, дата) и не может быть ссылкой на другой
Реляционной модели баз данных сопутствует реляционная алгебра, в которой определено много операций над отношениями. Три типичные операции - это выбор (selection), проекция (projection) и соединение (join).
Выбор выдает отношение, содержащее подмножество строк данного отношения, удовлетворяющее некоторому условию на значения полей. Применяя условие выбора " pages меньше, чем 400" к BOOKS, получим отношение, состоящее из первой, второй и последней строки BOOKS.
Проекция отношения на один или несколько атрибутов получается пропуском всех других полей и устранением повторяющихся строк в получившемся результате. Если спроектировать наше отношение на последний атрибут, то получим отношение с одним полем и с тремя кортежами: " STENDHAL ", " FLAUBERT " и " BALZAC ". Если же спроектировать его на три первых атрибута, то получится отношение с тремя полями, полученное из исходного вычеркиванием последнего столбца.
Соединение двух отношений это комбинированное отношение, полученное путем выбора атрибутов с AUTHORS ( АВТОРЫ ):
| Name (имя) | real_name (настоящее_имя) | Birth (год_ рождения) | death (год_ смерти) |
|---|---|---|---|
| "BALZAC" | "Honore_ de Balzac" | 1799 | 1850 |
| "FLAUBERT" | "Gustave Flaubert" | 1821 | 1880 |
| "PROUST" | "Marcel Proust" | 1871 | 1922 |
| "STENDHAL" | " |
1783 | 1842 |
Тогда
| title | date | pages | author/name | real_name | birth | death |
|---|---|---|---|---|---|---|
| "The Red and the Black" | 1830 | 341 | "STENDHAL" | " |
1783 | 1842 |
| "The Charterhouse of Parma" | 1839 | 307 | "STENDHAL" | " |
1783 | 1842 |
| "Madame Bovary" | 1856 | 425 | "FLAUBERT" | "Gustave Flaubert" | 1821 | 1880 |
| "Euge_nie Grandet" | 1833 | 346 | "BALZAC" | "Honore_ de Balzac" | 1799 | 1850 |
Реляционная модель допускает запросы - одно из главных требований к базе данных в нашем списке - в стандартизированном языке, называемом SQL. Они используются в двух формах: одна применяется непосредственно людьми, а другая ("
select title, date, pages from BOOKS
Он выдает названия, даты и число страниц для всех книг из таблицы BOOKS. Как мы видели, этот запрос в реляционной алгебре является
select title, date, pages, author where pages < 400
соответствует в реляционной алгебре выбору. Запрос:
select
title, date, pages, author, real_name, birth, date
from AUTHORS, BOOKS where
author = name
это внутреннее соединение, дающее тот же результат, что и приведенный пример соединения.
Основные понятия реляционных СУБД, кратко описанные выше, демонстрируют явное сходство с
Многие ОО-окружения предоставляют такую библиотеку для C++, Store ). Этот подход, который можно назвать объектно-реляционным взаимодействием, был успешно испробован во многих разработках. Он подходит в одном из следующих случаев:
Если ваши требования к сохраняемости не подпадают под эти случаи, то вы будете испытывать то, что в литературе называют сопротивлением несогласованности (impedance mismatch) между ОО-
Становление ОО-БД подкреплялось тремя стимулами.
Первый стимул наиболее очевиден для освоивших объектную разработку ПО, когда они сталкиваются с необходимостью сохранения объектов. Но он не обязательно является самым важным. Два других полностью относятся к области баз данных и не зависят от метода разработки.
Изучение понятия ОО-БД начнем с выявления ограничений реляционных систем D2 и того, чем они могут не устроить разработчиков ОО ПО (D1), а затем перейдем к новаторским достижениям движения за ОО-БД.
Было бы абсурдом отрицать вклад систем реляционных БД. (На самом деле в то время как первые публикации по ОО-БД в восьмидесятых склонялись к критике реляционной технологии, современная тенденция состоит в том, чтобы рассматривать эти два подхода как взаимодополняющие.) Реляционные системы являются одним из важнейших компонентов роста информационных технологий, начиная с семидесятых, и будут оставаться им еще долгое время. Они хорошо приспособились к ситуациям, связанным с данными (возможно, больших размеров), в которых:
Типичным примером является БД с данными о налогоплательщиках с большим количеством объектов, представляющих людей, описываемых фиксированными компонентами: ФИО (строка), дата рождения (дата), адрес (строка), зарплата (число) и еще несколько свойств.
Свойство (R3) исключает многие приложения, связанные с мультимедиа,
Как только у нас есть некоторый объект, ссылающийся на другой объект, то ОО-модель обеспечивает простой доступ к непрямым свойствам этого объекта. Например, redblack.author.birth_year возвращает значение 1783, если переменная redblack присоединена к объекту слева на рис. 13.5. Реляционное описание неспособно представить поле со ссылкой author ( автор ), чьим значением является обозначение другого объекта.
(рис 13.5) Объект со ссылкой на другой объектВ реляционной модели имеется обходной путь для этой проблемы, но он тяжелый и непрактичный. Для представления описанной ситуации будут необходимы два отношения BOOKS и AUTHORS, введенные выше. Для их связывания можно выполнить уже показанную операцию соединения, использующую соответствие между полями author первого отношения и name - второго.
Для ответа на вопросы вида: "В каком году родился автор "Красного и черного"?" реляционная реализация должна будет вычислять соединения, проекции и т. п. В данном случае можно использовать указанное выше соединение, а затем взять его проекцию на атрибут birth.
Этот метод работает и широко используется, но он применим только для простых схем. Число операций соединения быстро возрастает в сложных случаях для систем, постоянно обрабатывающих запросы со многими связями, например: "Сколько комнат имеется в предыдущем доме менеджера отдела, из которого дама, закончившая на первом месте среднюю школу вместе с младшим дядей моей жены, была переведена, когда компания-
Простота реляционной модели частично объясняется тем, что объекты однозначно идентифицируются значениями своих атрибутов. Отношение (таблица) является подмножеством A x B x ... некоторых множеств A, B, ...; иными словами, каждый элемент отношения, каждый объект, это a1, b1, ...>, в котором a1 принадлежит A и т. д. Поэтому он не существует вне своего значения, в частности, вставка объекта в отношение не будет иметь никакого эффекта, если в отношении уже имелся идентичный "The Red and the Black", 1830, 341, "STENDHAL" > в приведенное выше отношение BOOKS не приведет к изменению этого отношения. Это сильно отличается от динамичной модели ОО-вычислений, в которой могут существовать два идентичных объекта.
Напомним, что отношение истинно, если obj1 и obj2 - это ссылки, присоединенные к этим объектам, но равенство obj1 = obj2 будет ложным.
Быть идентичными - не значит быть одними и теми же (спросите об этом близнецов). Такая способность различать два этих понятия частично определяет силу моделирования в ОО-технологии. Она основана на понятии идентичности объекта: всякий объект существует независимо от его содержания.
(рис 13.6) Отдельные, но равные (обе нижние ссылки присоединены к одному объекту)Посетителям Императорского дворца в Киото говорят, что эти здания очень древние и каждое перестраивается приблизительно раз в сто лет. С учетом понятия идентичности объекта в этом нет никакого противоречия: объект остается тем же, даже если его содержание меняется. Вы та же личность, что и десять лет назад, хотя ни одной из молекул, составляющих ваше тело в то время, сейчас не осталось. |
Разумеется, в реляционной модели тоже можно выразить идентичность объектов: достаточно добавить к каждому объекту специальное
При создании ОО-ПО, не требующего хранить объекты, поддержка идентичности объекта получается почти случайно: в простейшей реализации каждый объект постоянно хранится по некоторому адресу, а ссылки на объект используют этот адрес, который служит неизменяемым идентификатором данного объекта. (Это неверно для более сложных реализаций, например, фирмы ISE, которые могут перемещать объекты в процессе эффективного сбора мусора; в этих реализациях идентичность объекта является более абстрактным понятием.) Если требуется хранить объекты, то идентичность объекта становится важным фактором ОО-модели.
Поддержка идентичности объектов в разделяемых БД приводит к новым проблемам: каждый клиент, которому нужно создавать объекты, должен получать для них уникальные идентификаторы; это значит, что специальный модуль, ответственный за присвоение идентификаторов, должен быть
Из предыдущих обсуждений можно вывести то, что может быть названо пороговой моделью ОО-БД: минимальное множество свойств, которым должна удовлетворять система БД, чтобы заслужить название ОО-БД (по работе [Zdonik 1990]). (Другие, также весьма желательные, свойства будут обсуждены ниже.) Имеется четыре требования, которым должна удовлетворять пороговая модель: база данных,
Примечательно, что в этом списке отсутствуют некоторые ОО-механизмы, необходимые для этого метода, в частности наследование. Но это не так странно, как может показаться на первый взгляд. Все зависит от того, что мы ожидаем от БД. Система на пороговом уровне должна быть хорошей машиной ОО-БД, предоставляющей набор механизмов для сохранения, возвращения и обхода структур объектов, но оставляющей знания более высокого уровня о семантике этих объектов (например,
Опыт ранних систем ОО-БД подтверждает, что подход |
Имеется много желательных свойств БД, не входящих в пороговую модель. Большинство коммерческих систем предлагают, по крайней мере, некоторые из них.
Первая категория включает непосредственную поддержку более глубоких свойств ОО-метода: наследования (одиночного или множественного),
Так называется способность запоминать предыдущие состояния объекта после, того, как вызовы процедур его изменили. Это особенно важно в случае параллельного доступа. Предположим, что объект O1 содержит ссылку на объект O2. Клиент изменяет некоторые поля O1, отличные от этой ссылки. Другой клиент изменяет O2. Тогда, если первый клиент попытается проследовать по ссылке, он может обнаружить версию O2, несовместную с O1.
Некоторые ОО-СУБД справляются с этой проблемой, трактуя каждую модификацию объекта как создание нового объекта, тем самым, поддерживая доступ к старым версиям объектов.
Объекты - это не единственные элементы, для которых требуется поддержка версий: со временем могут изменяться и порождающие их классы. Это проблема эволюции схемы, которая обсуждалась в начале этой лекции. Только очень немногие ОО-СУБД полностью поддерживают эволюцию схем.
Понятие транзакции уже давно является очень важным для СУБД, но классические механизмы транзакций ориентированы на короткие транзакции, которые начинаются и завершаются одной операцией, выполняемой одним пользователем во время одной сессии работы компьютерной системы. Исходным примером, процитированным в начале этой лекции, служит банковский перевод денег с одного счета на другой; он является транзакцией, поскольку требует либо полного выполнения обеих операций (снятия денег с одного счета и зачисления их на другой), либо (при неудаче) - сохранения исходного состояния. Время, занимаемое этой транзакцией, составляет несколько секунд (даже меньше, если не учитывать взаимодействие с пользователем).
У приложений, связанных с проектированием сложных систем, таких как
Область разработки ПО имеет очевидную потребность в длинных транзакциях, возникающую всякий раз, когда несколько человек или команд работают над общим набором модулей. Интересно, что технология БД не получила широкого распространения (несмотря на многие предложения в литературе) в сфере разработки ПО. Вместо этого, разрабатывались собственные средства
Хотя длинные транзакции концептуально могут и не требовать использования объектной технологии, усилия последнего времени по их поддержке пришли со стороны ОО-СУБД, некоторые из которых предлагают способ проверки любого объекта как в базе данных, так и вне нее.
Каждая СУБД должна предоставлять некоторую форму блокировки объектов для того, чтобы обеспечить безопасный параллельный доступ и обновление. Ранние ОО-СУБД поддерживали блокировку на уровне страниц, при которой границы блокируемой области определялись операционной системой. Это было неудобно как для больших объектов (которые могут занимать несколько страниц), так и для маленьких (которых может быть много на одной странице, так что блокировка одного из них повлечет блокировку и остальных). Новые системы предлагают блокировку на уровне объекта, позволяя приложению клиента блокировать объекты индивидуально.
Последние усилия направлены на минимизацию размера блокировки в процессе реального выполнения, поскольку блокировка может вызвать конфликты и замедление выполнения операций БД. Оптимистическая блокировка - это общее название целого класса методов, которые пытаются устранить априорное навешивание замка на объект, выполняя вместо этого спорные операции на копии объекта, откладывая насколько возможно обновление главной копии, затем блокируют ее, согласовывая конфликтующие обновления. Мы увидим далее пример оптимистической блокировки в системе Matisse.
Как было подчеркнуто выше, СУБД поддерживают запросы. Здесь ОО-системы в случае эволюции схем могут оказаться более гибкими, чем реляционные. Изменение схемы реляционной БД часто означает необходимость изменения текстов запросов и их перекомпиляцию. В ОО-БД запросы формулируются относительно объектов; вы спрашиваете о некоторых компонентах экземпляров некоторого класса. Здесь в качестве экземпляров могут выступать как прямые экземпляры данного класса, так и экземпляры его собственных потомков. Поэтому, если у класса, для которого сформулирован запрос, появляется новый потомок, то данный запрос применим и к экземплярам этого нового класса и позволяет извлекать из них требуемую информацию.
Начиная с середины восьмидесятых появилось большое число продуктов с ОО-СУБД. Некоторыми из наиболее известных являются: Gemstone, Itasca, Matisse, Objectivity, ObjectStore, Ontos, O2, Poet, Versant. Недавно несколько компаний, таких как UniSQL, разработали объектно-реляционные системы, пытаясь объединить наилучшие черты обоих подходов. Главные производители реляционных СУБД также предлагают или анонсируют комбинированные решения, такие как Illustra фирмы
Чтобы облегчить возможность взаимодействия, многие производители ОО-СУБД объединили свои силы в Object
Давайте взглянем на две особенно интересные системы: Matisse и Versant.
MATISSE от фирмы
Matisse - это смелая разработка со многими необычными идеями. Она ориентирована на большие базы данных с богатой семантической структурой и может манипулировать с очень большими объектами, такими как изображения, фильмы и звуки. Хотя она поддерживает основные ОО-понятия, в частности,
Matisse использует оригинальный подход к проблеме минимизации блокировок. Многие системы применяют следующее правило взаимного исключения: несколько клиентов могут читать объект одновременно, но как только один из клиентов начинает писать, ни один из других не может читать или писать. Причина, объясненная в лекции о параллельности, состоит в сохранении целостности объекта, выраженной инвариантами класса. Если разрешить одновременную запись двум клиентам, то объект может стать несовместным, а если некоторый клиент находится в середине процесса записи, то объект может оказаться в нестабильном состоянии (не удовлетворяющем инварианту), так что другой клиент, который его в этот момент читает, получит неверный результат.
Очевидно, что блокировка вида писатель-писатель необходима. Что касается исключений вида писатель-читатель, то некоторые системы их не придерживаются, разрешая операции чтения даже при наличии блокировки записи. Такие операции уместно назвать
Matisse, чьи создатели явно преследовали цель минимизации блокировок, предложил радикальное решение этого вопроса, основанное на организации управления объектами: никаких операций записи. Вместо изменения существующего объекта операция записи (из ПО клиента) создает новый объект. В результате можно читать объекты без всяких блокировок: у вас всегда будет доступ к некоторой версии БД, на которую не повлияли операции записи, которые могли произойти после начала вашего чтения. Вы также можете получить доступ к большому числу объектов с гарантией, что все они принадлежат одной и той же версии БД, в то время как при традиционном подходе для достижения того же результата потребовалось бы использовать глобальные блокировки или транзакции, что привело бы к большой потере эффективности.
Следствием такой политики является возможность возврата к предыдущим версиям объекта или самой БД. По умолчанию, старые версии сохраняются, но система предоставляет "сборщик версий", позволяющий избавляться от нежелательных версий.
Система Matisse предоставляет интересные возможности для работы с отношениями. Например, если у класса EMPLOYEE ( СЛУЖАЩИЙ ) имеется атрибут ( руководитель ): MANAGER, то Matisse (по требованию разработчика) автоматически отслеживает обратные связи, так что можно получить доступ не только к руководителю служащего, но также и ко всем служащим, подчиненным данному руководителю. Кроме того, возможны запросы, ищущие объекты по ключевым словам.
Versant от фирмы Versant
Versant - это одна из СУБД, отвечающих стандарту
При разработке Versant особое внимание было уделено блокировке и транзакциям. В ней можно блокировать отдельные объекты. Приложение может запросить чтение, обновление или запись заблокированного объекта. Обновления служат для устранения взаимных блокировок: если вам нужно прочесть заблокированный объект и записать в него, то требуется вначале запросить право на его обновление, предоставляемое при условии, что никакой другой клиент им в данный момент не пользуется. При этом остальные клиенты могут читать объект, пока не начнется (гарантированное) выполнение вашего запроса на запись. Непосредственный переход от чтения заблокированного объекта к записи мог бы привести к взаимной блокировке: каждый из двух клиентов мог бы ждать до бесконечности, пока другой не снимет свою блокировку.
Механизм транзакций обеспечивает как короткие, так и длинные транзакции; приложение может оставить объект на любое время. Поддерживаются версии объектов и
Другой особенностью Versant является ее механизм, позволяющий сообщать приложению о различных событиях в БД, например, об удалении или обновлении объекта. Получив такое извещение, приложение может выполнить предусмотренные на этот случай действия.
СУБД Versant предоставляет пользователям богатый набор типов данных, включая и множество заранее определенных коллекций классов. Это позволяет проводить эволюцию схемы при условии, что новые поля инициализируются предопределенными значениями. В ней также имеются возможности
Завершим этот обзор мечтами о возможных направлениях развития сохраняемости в будущем. Следующие далее наблюдения носят предварительный характер, их цель - побудить дальнейшие размышления, а не предложить конкретные ответы.
Понятие базы данных произошло от взгляда на мир, в центре которого сидят Данные, а расположенным вокруг программам разрешены доступ и модификация этих Данных:
(рис 13.7) Взгляд со стороны баз данныхОднако в объектной технологии мы научились понимать данные как сущности, полностью определяемые применяемыми к ним операциями:
(рис 13.8) ОО-взглядЭти два взгляда кажутся несовместимыми! Понятие данных, существующих независимо от обрабатывающих их программ ("независимость данных", догмат, повторяемый на первых страницах любой книги по БД) является проклятием для ОО-разработчика. Должны ли мы считать выражение "ОО-база данных"
Возможно, нет, но, быть может, стоит понять, как в догматическом ОО-контексте можно получить эффект баз данных, реально не имея их. Если мы дадим определение (упрощая до самого существенного данное ранее в этой лекции определение БД)
БАЗА ДАННЫХ = СОХРАНЯЕМОСТЬ + РАЗДЕЛЕНИЕ ДАННЫХ,
то догматический взгляд будет рассматривать второй компонент, разделение данных, как несовместимый с ОО-идеями, и сосредоточится только на сохраняемости. Но тогда можно подойти к разделению данных, используя другой метод - параллелизм! Это показано на следующем рисунке.
(рис 13.9) Отделение сохраняемости от разделения данныхСледуя ОО-принципам, сохраняемые данные реализуются как множество объектов - экземпляров некоторых абстрактных типов данных - и управляются некоторой серверной системой. Системы клиентов, которым требуется работать с данными, будут делать это через сервер. Так как эта схема требует разделения и параллельного доступа, то клиенты будут рассматривать сервер как сепаратный в смысле, определенном при обсуждении параллельности в лекции 12. Например:
flights: separate FLIGHT_DATABASE; ...
flight_details (f: separate FLIGHT_DATABASE;
rf: REQUESTED_FLIGHTS): FLIGHT is
do
Result := f.flight_details (rf)
end
reserve (f: separate FLIGHT_DATABASE; r: RESERVATION) is
do
f.reserve (r); status := f.status
end
Тогда на стороне сервера не требуется никакого механизма разделения, а только общий механизм сохранения. Нам могут также понадобиться средства и методы для поддержки версий объектов, но они, по существу, относятся к вопросам сохраняемости объектов, а не к базам данных.
В этом случае механизм сохранения может стать чрезвычайно простым, отбросив многое из багажа БД. Можно даже считать, что все объекты по умолчанию являются постоянно хранимыми, а временные объекты становятся исключением, обрабатываемым механизмом, обобщающим сбор мусора. Такой подход, который невозможно было представить при изобретении БД, становится менее абсурдным при постоянном уменьшении стоимости памяти и росте доступности 64-битовых виртуальных адресных пространств, в которых, как было уже замечено в [Sombrero-Web], " можно создавать каждую секунду новый 4-гигабайтный объект (вся память обычного 32-битного процессора) в течение 136 лет и все еще не исчерпать доступные адреса. Этого достаточно, чтобы сохранить все данные, связанные с почти любым приложением на протяжении всего его существования ".
Все это весьма умозрительно и нет никаких оснований для того, чтобы отказываться от традиционного понятия БД. Не следует также немедленно мчаться и продавать акции компаний, создающих ОО-БД. Рассматривайте это обсуждение как интеллектуальное упражнение: приглашение прозондировать будущее общепринятого понятия ОО-БД, проверив, может ли существующий подход по-настоящему успешно справиться со страшным сопротивлением несогласованности между методом разработки ПО и механизмами, поддерживающими накопление и хранение данных.
Последнее замечание о БД. Взрывной рост Интернета и появление средств поиска, основанного на контексте (в момент написания книги наиболее известными примерами таких средств были AltaVista, Web
СУБД требуют, чтобы перед сохранением любых данных вы сначала конвертировали их в строго определенный формат схемы БД. Недавние исследования, тем не менее, показали, что 80% электронных данных в компаниях являются неструктурированными (т. е. располагаются вне БД, как правило, в текстовых файлах), несмотря на многолетнее
Пока еще ответ - да. Неструктурированные и структурированные данные будут сосуществовать. Но БД больше не являются единственной выбором; все более и более изощренные средства для запросов смогут извлекать информацию, даже если она не имеет формата, требуемого БД. Разумеется, для создания таких средств лучше всего подходит ОО-технология.
Первой работой о реляционной модели была статья [Codd 1970]; имеется множество книг по этой тематике. По-видимому, наиболее известный учебник по БД c упором на реляционную модель - книга [Date 1995] (это шестое издание книги, впервые опубликованной в середине семидесятых). Другой полезный текст общего назначения - [Elmasri 1989].
[Walden 1995] содержит детальное практическое обсуждение того, как выполнять работу по объектно-реляционному взаимодействию. [Khoshafian 1986] выдвигает вопрос об идентификации объектов на передний план обсуждения ОО-БД.
Хорошей отправной точкой для понимания целей ОО-БД и чтения некоторых оригинальных статей является сборник [Zdonik 1990], включающий работы некоторых пионеров этой области, а его вводная лекция явилась источником понятия "пороговой модели", использованного в этой лекции. Широко циркулирующий "Манифест ОО-систем баз данных" [Atkinson 1989], являющийся результатом сотрудничества большого числа экспертов, существенно повлиял на цели движения ОО-БД. Сейчас имеется много учебников по этой тематике, перечислим некоторые из наиболее известных (в порядке публикации): [Kim 1990], [Bertino 1993], [Khoshafian 1993], [Kemper 1994], [Loomis 1995]. Дальнейшие постоянно обновляемые ссылки можно найти в он-лайн библиографии Майкла Лея (Michael Ley) по системам БД [Ley-Web]. Группа Клауса Дитриха (Klaus Dittrich) в Цюрихском университете ведет список часто задаваемых вопросов о ОО-БД http://www.ifi.unizh.ch/groups/dbtg/ObjectDB/ ODBminiFAQ.html. В [Cattel 1993] описан стандарт
Эта лекция очень выиграла от важных комментариев Ричарда Биляка ( Richard Bielak), особенно об эволюции схем, Замыкании Сохраняемости, запросам к ОО-БД, Versant и Sombrero. Описание Versant базируется на [Versant 1994], а Matisse - на [
Предварительный вариант материала этой лекции, посвященного эволюции схем, появился в [M 1996c]. Обсуждение вопроса о том, как соответствуют друг другу ОО-понятия и понятия БД, пришло из неопубликованных заметок лекций, прочитанных в 1995 г. на конференциях TOOLS USA и European Software Engineering Conference [M 1996d].
Предложите, как расширить методы эволюции схем, развитые в этой лекции, чтобы учесть случай, при котором классы программной системы могут изменяться во время выполнения системы.
Обсудите, в каком виде могут формулироваться запросы в ОО-БД.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.