Задача: в проекте есть набор классов со сложной внутренней структурой.
Требуется реализовать принцип сохранения состояния объектов этих классов и восстановления этого состояния.
Проблема только, что сейчас ещё неизвестно куда они будут сохраняться: XML, база данных (тоже неизвестно какая), может ещё что - а может нужно предложить пользователю все возможные варианты, чтобы сам выбрал.
Вполне возможно, что будут появляться новые варианты уже после разработки и передачи системы заказчику.
Т.е. сама операция "сохранения" (сериализации) будет реализована уже после реализации (и даже компиляции в проект) классов.
Видимо это будет в виде shared library реализовано, но вот как его "внедрить" в архитектуру проектов на уровне классов
Была мысль перегрузить operator>> и operator<< для каждого класса, причём несколько раз (для XML, для MYSQL*, PGSQL* и т.п.).
Но тогда не получается реализовать идеологию c shared library: эти операторы же уже должны быть известны на стадии компиляции проекта.
Никто похожими мыслями не озадачивался? Как решали?
"Сериализация" объектов проекта. (destination сериализации на этапе разработки неизвестен)
Модератор: Модераторы разделов
-
v04bvs
- Сообщения: 636
- ОС: Debian GNU/Linux
Re: "Сериализация" объектов проекта.
Код: Выделить всё
abstract class AbstractSerializator {
public abstract void save(String name, int value);
public abstract void save(String name, double value);
public abstract void save(String name, Object value);
}
interface Serializable {
void serialize(AbstractSerializator serializator);
}
class Hero implements Serializable {
private double health;
private int strength;
private String name;
public void serialize(AbstractSerializator serializator) {
serializator.save("health", health);
serializator.save("strength", strength);
serializator.save("name", name);
}
}Принцип думаю понятен.
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: "Сериализация" объектов проекта.
Основных подхода видется два.
Либо мы сохраняем всю структуру в "универсальный" формат (ХМЛ), плагин-конвертер (один из) его получает, пишет целиком как есть в... (файл, сокет, BLOB и т.д.) или берет хмл-евую либу, проходит и делает-конвертирует чего надо (раскладывает по полям для SQL и т.д.)
Второй вариант - задействуем стандартный дизайн-темплейт, когда относительно "корня" структуры вызывается метод, ему передается ссылка на интерфейс,
и метод обеспечивает рекурсивный обход своего дерева, передавая каждый элемент интерфейсу.
или
Оч примерное описание, нормальное было в книжке у "Банды четырех".
Ограничение - структура может быть любой, но набор элементов из которых она состоит, должен быть постоянен (к моменту разработки Worker-ов), иначе придется править-пересобирать их все. Ограничений на способы обработки структуры соотв-но нет.
У operator << есть один недостаток: он всегда бинарен. Его нельзя перегрузить по дополнительному аргументу, нужно всегда плодить детей ostream. Кроме того, применение его с чем-то кроме istream+/ostream+ - уже дурной тон, пожалуй. А в общем случае, ничего кроме совместимости std::i/o-stream_iterator это не дает. Так что на мой вкус правильнее решение типа
S& write(S& out, const T& obj) { return obj.write(out) ; }
и
virtual S& T::write(S&) const ;
соотв-но. К этой бурде можно доделать перегруженный по доп.аргументу (state, format etc.) всегда...
Либо мы сохраняем всю структуру в "универсальный" формат (ХМЛ), плагин-конвертер (один из) его получает, пишет целиком как есть в... (файл, сокет, BLOB и т.д.) или берет хмл-евую либу, проходит и делает-конвертирует чего надо (раскладывает по полям для SQL и т.д.)
Второй вариант - задействуем стандартный дизайн-темплейт, когда относительно "корня" структуры вызывается метод, ему передается ссылка на интерфейс,
Код: Выделить всё
class Worker {
virtual void process(const TypeA&) = 0;
virtual void process(const TypeB&) = 0;
virtual void process(const TypeC&) = 0;
};и метод обеспечивает рекурсивный обход своего дерева, передавая каждый элемент интерфейсу.
Код: Выделить всё
w.process(* this);или
Код: Выделить всё
w.process(this->_number);Оч примерное описание, нормальное было в книжке у "Банды четырех".
Ограничение - структура может быть любой, но набор элементов из которых она состоит, должен быть постоянен (к моменту разработки Worker-ов), иначе придется править-пересобирать их все. Ограничений на способы обработки структуры соотв-но нет.
У operator << есть один недостаток: он всегда бинарен. Его нельзя перегрузить по дополнительному аргументу, нужно всегда плодить детей ostream. Кроме того, применение его с чем-то кроме istream+/ostream+ - уже дурной тон, пожалуй. А в общем случае, ничего кроме совместимости std::i/o-stream_iterator это не дает. Так что на мой вкус правильнее решение типа
S& write(S& out, const T& obj) { return obj.write(out) ; }
и
virtual S& T::write(S&) const ;
соотв-но. К этой бурде можно доделать перегруженный по доп.аргументу (state, format etc.) всегда...
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
-
Zeus
- Сообщения: 694
Re: "Сериализация" объектов проекта.
Спасибо за советы.
Мысль с плагином-конвертером понравилась.
"Научить" классы сохраняться/читаться (хоть операторами, хоть save/load'ами) из древовидной структуры, типа этой: XML парсер в DOM tree. C++
а потом - только и делай, что конвертеры пиши
Боюсь только, не слишком ли расточительны будут все эти преобразования туда-сюда при большом количестве данных?
Может лучше всё-таки сделать абстрактный базовый класс saver/loader, а в shared libraries реализовывать его наследников для всяких разных случаев (XML, база, файл, сокет), которые, зная структуру класса-хранилища, будут напрямую шастать по его потрохам и сохранять данные?
А что там про недостаток операторов << / >>?
Про унарность - понятно.
А про детей и итераторы что-то недопонял
Мысль с плагином-конвертером понравилась.
"Научить" классы сохраняться/читаться (хоть операторами, хоть save/load'ами) из древовидной структуры, типа этой: XML парсер в DOM tree. C++
а потом - только и делай, что конвертеры пиши
Боюсь только, не слишком ли расточительны будут все эти преобразования туда-сюда при большом количестве данных?
Может лучше всё-таки сделать абстрактный базовый класс saver/loader, а в shared libraries реализовывать его наследников для всяких разных случаев (XML, база, файл, сокет), которые, зная структуру класса-хранилища, будут напрямую шастать по его потрохам и сохранять данные?
А что там про недостаток операторов << / >>?
Про унарность - понятно.
А про детей и итераторы что-то недопонял
-
sergio
- Сообщения: 436
- Статус: Интересующийся новичок
- ОС: Debian GNU/Linux 4 & 5
Re: "Сериализация" объектов проекта.
Главное - все не проклясть при этом процессе, если его делать приде тся тебе же.
А что там про недостаток операторов << / >>?
Код: Выделить всё
enum { TEXT_SUMMARY, FULL_DUMP, TEXT_XML };
template <int N> class FormatTag { };
write(std::cout, my_obj, FormatTag<TEXT_SUMMARY>());
write(std::cerr, my_obj, FormatTag<FULL_DUMP>());
std::ofstream ou("file.xml");
write(ou, my_obj, FormatTag<TEXT_XML>());Примерно так. А с operator << мы ограничены одним вариантом. Для остальных надо или давать другие имена функциям/методам (соотв-но плачет однообразие и кое-где шаблоны), или плодить производные классы "потока вывода", что иногда само-собой в любом случае приходится делать, а иногда и вовсе ни к чему. А вешание вывода на operator << вроде б позволяет из стандартной либы только вот это задейстововать:
Код: Выделить всё
std::copy(container.begin(), container.end(), std::ostream_iterator<Type>(cout, ", ");Не могу сообразить, что в стдлиб это где-то еще применялось.
Одним словом - вопрос вкуса и задачи. Можно так, можно этак.
Если не ХМЛ а код, имхо главное - сообразить, как построить интерфейс. Т.е. вопрос, может ли внешний код, который получает структуру для вывода, сам по ней пройти, или его нужно проводить вызывая для элемента; есть ли аксесс-методы ко всем данным, которые ему нужны, или объект должен скармливать ему данные сам поштучно; плюс надо отобразить иерархию - т.е. о перемещении по дереву конвертор должен получать информацию тоже, если не сам по дереву идет, т.е. типа:
Код: Выделить всё
SomeNode::serialize(Serializer s) {
s.beginNode(this->_class_id);
for each child
child.serialize(s);
s.endNode();Плюс надо сразу думать о обратном процессе, но скорее всего не проще.
Debian GNU/Linux 4 -- AMD Athlon64 3000+ / Asus 7600GS -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome
Debian GNU/Linux 5 -- Dell (Vostro) 500 (Celeron M560 / iGM965) -- Gnome