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