Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4086 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 13:

Сохранение объектов и базы данных (БД)

Аннотация: Выполнение ОО-приложения означает создание и манипулирование некоторым числом объектов. Что происходит с этими объектами, когда текущее вычисление завершается? Часть объектов исчезает с завершением сессии. Многим приложениям нужны также и сохраняемые объекты, остающиеся между сессиями. Сохраняемые объекты могут использоваться разными приложениями, что приводит к необходимости баз данных.
Ключевые слова: механизмы, язык программирования, БД, ПО, составной объект, замыкание, retrieval, абстрактный синтаксис, abstract syntax tree, AST, код компилятора, сервер приложений, vision, архивирование, 1-грамматика, preservation, RESTORE, управление конфигурацией, OLE, структурный подход, атрибут класса, mismatch, retry, входной, объект, доступ, система управления базой данных, СУБД, Persistence, programmability, arbitrary, контроль доступа, access control, абстрактное свойство, ограничение целостности, integrity constraints, текущий контроль, архивация, кредит, список, кортеж, согласование типов, henry, соединение отношений, встроенный SQL, операция проекции, основная модель, операции реляционной алгебры, Smalltalk, модель данных, реляционная модель данных, CAD/CAM, нормальная форма, учредитель, декартово произведение, equalizer, ключевое поле, разделяемый ресурс, инкапсуляция, отношение наследования, машина баз данных, полиморфизм, типизация, динамическое связывание, свойство транзакции, configuration management, функции субд, Informix, Oracle, Object, database manager, group, ODMG, языками запросов, ADB, множественное наследование, архитектура клиент-сервер, грязное чтение, dirty read, supervisor, object technology, оптимистическая блокировка, механизм "запрос-ответ", индексация, oxymoron, crawler, yahoo!, использование базы данных, гигабайт, DATE, манифест, группа, tools, software, engineering, conference

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

Во-вторых, они могут объединить объектную технологию с наиболее доступным видом (не ОО) БД - реляционными базами данных. И, в-третьих, можно использовать новые объектно-ориентированные системы баз данных, которые переносят на БД идеи ОО-технологии. Эти методы по очереди описываются в данной лекции, в ней также имеется обзор технологии ОО-баз данных с упором на два наиболее известных продукта. Завершается лекция обсуждением гипотетической будущей судьбы баз данных в ОО-контексте.

Сохраняемость средствами языка

Для удовлетворения многих потребностей в сохраняемости достаточно иметь связанный с окружением разработки набор механизмов для записи объектов в файлах и их чтения. Для простых объектов, таких как числа или символы, можно использовать средства ввода-вывода, аналогичные средствам традиционного программирования.

Сохранение и извлечение структур объектов

Как только появляются составные объекты, простое запоминание и извлечение индивидуальных объектов становится недостаточным, поскольку в них могут находиться ссылки на другие объекты, а объект, лишенный своих связников (см. лекциию 8 курса "Основы объектно-ориентированного программирования"), некорректен. Это наблюдение привело нас в лекции 8 курса "Основы объектно-ориентированного программирования" к принципу Замыкания Сохраняемости, утверждающему, что всякий механизм сохранения и возвращения должен обрабатывать вместе с некоторым объектом всех его прямых и непрямых связников, что показано на рис. 13.1.

Необходимость в замыкании при сохранении

Рис. 13.1. Необходимость в замыкании при сохранении

Принцип Замыкания Сохраняемости утверждает, что всякий механизм, сохраняющий O1, должен также сохранить все объекты, на которые он непосредственно или опосредованно ссылается, иначе при извлечении этой структуры можно получить бессмысленное значение ("висящую ссылку") в поле loved_one объекта O1.

Мы рассмотрели механизмы класса STORABLE, предоставляющие соответствующие средства: store для сохранения структуры объекта и retrieved для ее извлечения. Это ценный механизм, чье присутствие в ОО-окружении само по себе является большим преимуществом перед традиционными окружениями. В лекции 8 курса "Основы объектно-ориентированного программирования" приведен типичный пример его использования: реализация команды редактора SAVE. Вот еще один пример из практики нашей фирмы ISE. Наш компилятор выполняет несколько проходов по представлениям текста программы. Первый проход создает внутреннее представление, называемое Деревом Абстрактного Синтаксиса (Abstract Syntax Tree (AST)). Задача последующих проходов заключается в постепенном добавлении семантической информации в AST ("украшении дерева") до тех пор, пока ее не станет достаточно для генерации целевого кода компилятором. Каждый проход завершается операцией store ; а следующий проход начинается восстановлением AST с помощью операции retrieved.

Механизм STORABLE работает не только на файлах, но и на сетевых соединениях таких, как сокеты; он на самом деле лежит в основе библиотеки клиент-сервер Net.

Форматы сохранения

У процедуры store имеется несколько вариантов. Один, basic_store (базовое_сохранение), сохраняет объекты для их последующего возвращения в ту же систему, работающую на машине той же архитектуры, в процессе того же или последующего ее исполнения. Эти предположения позволяют использовать наиболее компактную форму представления объектов.

Другой вариант, independent_store (независимое_сохранение), обходится без этих предположений; представление объекта в нем не зависит от платформы и от системы. Поэтому оно занимает несколько больше места, так как использует переносимое представление для чисел с плавающей точкой и для других числовых значений, а также должно включать некоторую простую информацию о классах системы. Но он важен для систем типа клиент-сервер, которые должны обмениваться потенциально большими и сложными наборами объектов, находящимися на машинах весьма разных архитектур, работающих в различных системах. Например, сервер на рабочей станции и клиент на PC могут выполнять два разных приложения и взаимодействовать с помощью библиотеки Net. Сервер приложения выполняет основные вычисления, а приложение клиента реализует интерфейс пользователя, используя графическую библиотеку, например Vision.

Заметим, что только сохранение требует нескольких процедур - basic_store, independent_store. Хотя реализация операции возврата для каждого формата своя, всегда можно использовать один компонент retrieved, реализация которого определит формат возвращаемых данных, используемый в файле или сети, и автоматически применит соответствующий алгоритм извлечения.

Вне рамок замыкания сохраняемости

Принцип Замыкания Сохраняемости теоретически применим ко всем видам сохранения. Как мы видели, это позволяет сохранить совместность сохраненных и восстановленных объектов.

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

Такая проблема возникает, в частности, из-за разделяемых структур, как в следующем примере.

Требуется заархивировать сравнительно небольшую структуру данных. Так как она содержит одну или более ссылок на большую разделяемую структуру, то принцип замыкания сохраняемости требует архивирования и этой структуры. В ряде случаев этого делать не хочется. Например, как показано на рис. 13.1, объект личность может через поле 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,

где preserve - это тоже компонент ACTIONABLE, который куда-нибудь безопасно копирует объект. Тогда post_action будет выполнять вызов:

restore,

восстанавливающий объект из сохраненной копии.

В общем случае того же эффекта можно добиться с помощью вызова вида:

store_ignore ("address"),

где store_ignore получает в качестве аргумента имя поля. Так как реализация store_ignore может просто пропускать поле, устраняя необходимость двустороннего копирования посредством preserve и restore, то в данном случае это будет более эффективно, но механизм 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. Предложенный механизм, используемый с соблюдением указанных правил, позволит вам остаться верным духу принципа Замыкания Сохраняемости, делая его применение более гибким.