Имеем структуру объектов, у всех один родитель.
Задачи:
1. Сохранять/загружать структуру в файл (xml).
2. Копировать объекты
3. Удалять из структуры с возможностью отмены.
Мне кажется или есть во всех этих задачах что-то родственное? Например можно применить паттерн «состояние», а в нем реализовать загрузку/сохранение, копирование, копированием же достигается возможность восстановления после удаления.
Но что-то мне это кажется кашей, кроме «состояния» нужны еще какие-то сущности, например «стратегия» (у «состояния») для разных операций .
Ну если все по отдельности то трудность у меня вызывает «команда» удаление с возможностью отмены. Дело в том что в структуре один объект может использоваться множеством объектов, перед удалением объект посылает сообщения всем использующим его объектам, те же получив это сообщение очищают свои ссылки на удаляемый объект. Как их восстановить во время отмены удаления? Объекты могут получив от объекта сообщение об удалении записать в него(сообщение) свою «команду» очищения ссылок с возможностью отмены. Получается такая здоровая композитная «команда». Больше пока придумать ничего не могу. Какие есть более логичные способы? У меня где-то глубоко роится идея транзакций, но как их грамотно реализовать не знаю, опыта маловато.
Посоветуйте как мне лучше поступить, стоит ли заморачиваться над объединением всех этих задач в чем-то общем абстрактном или делать все по отдельности.
Здравствуйте, byterus, Вы писали:
B>Ну если все по отдельности то трудность у меня вызывает «команда» удаление с возможностью отмены. Дело в том что в структуре один объект может использоваться множеством объектов, перед удалением объект посылает сообщения всем использующим его объектам, те же получив это сообщение очищают свои ссылки на удаляемый объект. Как их восстановить во время отмены удаления? Объекты могут получив от объекта сообщение об удалении записать в него(сообщение) свою «команду» очищения ссылок с возможностью отмены. Получается такая здоровая композитная «команда». Больше пока придумать ничего не могу. Какие есть более логичные способы? У меня где-то глубоко роится идея транзакций, но как их грамотно реализовать не знаю, опыта маловато.
B>Посоветуйте как мне лучше поступить, стоит ли заморачиваться над объединением всех этих задач в чем-то общем абстрактном или делать все по отдельности.
Как это делали мы...
Есть своя сериализация.
Есть класс, отвечающий за храниение всех объектов — мы называем его проект.
Здравствуйте, byterus, Вы писали:
B>Ну если все по отдельности то трудность у меня вызывает «команда» удаление с возможностью отмены. Дело в том что в структуре один объект может использоваться множеством объектов, перед удалением объект посылает сообщения всем использующим его объектам, те же получив это сообщение очищают свои ссылки на удаляемый объект. Как их восстановить во время отмены удаления? Объекты могут получив от объекта сообщение об удалении записать в него(сообщение) свою «команду» очищения ссылок с возможностью отмены. Получается такая здоровая композитная «команда». Больше пока придумать ничего не могу. Какие есть более логичные способы? У меня где-то глубоко роится идея транзакций, но как их грамотно реализовать не знаю, опыта маловато.
B>Посоветуйте как мне лучше поступить, стоит ли заморачиваться над объединением всех этих задач в чем-то общем абстрактном или делать все по отдельности.
Извините за предыдущее сообщение-обрывок — случайно получилось.
Теперь как это делали мы (наша команда).
Есть своя сериализация. Есть класс, отвечающий за хранение в памяти всех объектов — мы называем его проект. Это аналог документа в MVC.
Любое изменение, удаление, создание объекта, принадлежащего документу, протоколируется.
За каждой командой хранится множество указателей на объекты, в этой команде созданные, изменённые, удалённые, добавленные к документу и убранные из документа.
Хотя последние 2 для Вас, наверное, лишнее. Также за командой хранятся файлы на диске, куда пишется старое состояние изменённых и удалённых объектов.
Вот как выглядит, например, удаление узла дерева:
1. Удаляём узел по адресу X. Сохраняем в команде указатель на него (X) и его содержимое.
2. Обновляем связи. Сохраняем в команде указатели на изменённые объекты и их содержимое.
Undo.
1. Создаём удалённый объект по адресу Y.
2. Читаем состояние объектов (изменённых и удалённого), заменяя на ходу X -> Y.
Если я вас правильно понял то объекты физически в вашей системе не удаляются. В команде сохраняется положение объекта в дереве и состояние всех зависимых от удаляемого объекта объектов.
Предположим ситуацию, удаляемый объект является контейнером небольших но очень часто используемых в проекте объектов, причем сами объекты в свою очередь тоже могут являться контейнерами для других часто используемых объектов. Как ваша система поступит при удалении этого объекта?
Как я уже говорил у меня есть идея о транзакциях вот как я ее понимаю:
У каждого проекта в системе имеется CommandManager который отвечает за механизм запуска команд и реализацию Undo\Redo если команда его поддерживает.
Исходное положение — система находится в спокойствии. Наступает событие – нажата кнопка Del для удаления выделенного объекта.
Проект передает CommandManager`у команду удаления объекта. CommandManager видит что команда поддерживает транзакции, открывает этой командой транзакцию и делает выполнение этой команды(метод Execute)
Задача команды всего лишь сохранить состояние удаляемого объекта и вызвать его метод Delete. При уничтожении объект если это контейнер вызывает для всех своих дочерних объектов команду на удаление. CommandManager принимает все команды и если открыта транзакция записывает их в нее, причем даже вложенные команды могут быть транзакциями. Так как при удалении объекты оповещают об этом всех объектов от них зависимых то те в свою очередь запускают свои команды которые попадают в текущую транзакцию. Когда все циклы закончены начальная команда закрывается и система опять в спокойствии. В системе должны присутствовать три типа команд:
1. Просто команды, например открытие закрытие документа
2. Команды с поддержкой отката, например ввод символа
3. Команды транзакции с поддержкой отката, которые являются контейнерами для других команд в т.ч. и транзакций, например удаление объекта.
Что вы думаете по этому поводу, понятно что все зависит от реализации, но в принципе такой механизм возможен?
Здравствуйте, byterus, Вы писали:
B>Здравствуйте, What, Вы писали:
B>Если я вас правильно понял то объекты физически в вашей системе не удаляются. В команде сохраняется положение объекта в дереве и состояние всех зависимых от удаляемого объекта объектов.
Нет. Объекты удаляются физически из памяти. Перед удалением они сериализуются в файловый архив.
B>Предположим ситуацию, удаляемый объект является контейнером небольших но очень часто используемых в проекте объектов, причем сами объекты в свою очередь тоже могут являться контейнерами для других часто используемых объектов. Как ваша система поступит при удалении этого объекта?
Наша система не расчитана на составные объекты. Любой объект может ссылаться на любой другой, но хозяин всех объектов только один — проект. В принципе, можно реализовать Undo/Redo и когда сам объект является контейнером небольших, но очень часто используемых в проекте объектов, однако, ИМХО, это, во-первых, не очень нужно, а, во-вторых, усложнит сам механизм. Точно такой же функциональности можно достичь, когда "объект-контейнер" хранит указатели на свои "под-объекты" и при своём удалении обращается к проекту и удаляет их.
B>Как я уже говорил у меня есть идея о транзакциях вот как я ее понимаю:
B>У каждого проекта в системе имеется CommandManager который отвечает за механизм запуска команд и реализацию Undo\Redo если команда его поддерживает.
B>Исходное положение — система находится в спокойствии. Наступает событие – нажата кнопка Del для удаления выделенного объекта.
B>Проект передает CommandManager`у команду удаления объекта. CommandManager видит что команда поддерживает транзакции, открывает этой командой транзакцию и делает выполнение этой команды(метод Execute) B>Задача команды всего лишь сохранить состояние удаляемого объекта и вызвать его метод Delete. При уничтожении объект если это контейнер вызывает для всех своих дочерних объектов команду на удаление. CommandManager принимает все команды и если открыта транзакция записывает их в нее, причем даже вложенные команды могут быть транзакциями. Так как при удалении объекты оповещают об этом всех объектов от них зависимых то те в свою очередь запускают свои команды которые попадают в текущую транзакцию. Когда все циклы закончены начальная команда закрывается и система опять в спокойствии. В системе должны присутствовать три типа команд: B>1. Просто команды, например открытие закрытие документа B>2. Команды с поддержкой отката, например ввод символа B>3. Команды транзакции с поддержкой отката, которые являются контейнерами для других команд в т.ч. и транзакций, например удаление объекта.
B>Что вы думаете по этому поводу, понятно что все зависит от реализации, но в принципе такой механизм возможен?
В целом, да. Однако есть несколько сомнений/вопросов:
1. Действительно ли нужна такая "навороченность" механизма? По-моему, достаточно, когда вся информация сохраняется в команде. То есть, в команде удаления объекта фиксируется содержимое удалённого и изменённых объектов и их адреса. Хотя макрокоманды (состоящие из нескольких команд), скорее всего всё равно будут нужны.
2. При выполнении Undo будет читаться состояние объектов, которые указывали на удалённый объект. Но при создании удалённого объекта в памяти он сядет по другому адресу — нужно будет перекидывать адреса. Более того, в следующей команде может быть создан другой объект, который сядет по тому же адресу, что и удалённый в предыдущей. Поэтому "перекидывание адресов" нужно полностью расписать, прежде чем садиться за реализацию.
3. Сколько кода надо будет написать для выполнения такой простой операции, как удаление объекта? В конечном счёте, если ваша система будет долго развиваться, очень важно, чтобы написание новых команд было простым. Если Вы пишите на C++, было разумно применить умные указатели, которые бы упростили взаимодействие с CommandManager'ом.
4. Как показал опыт, возникает много проблем с неправильным использованием такого механизма команд (например, объект создал, а CommandManager'у/Команде забыл об этом сообщить). Поэтому желательно заранее продумать отладочные механизмы (у нас они заняли ~ 50% кода). И опять же, если вы пишите на C++, можно попробовать часть проверки ошибок перенести на этап компиляции. Об этом — см. Элджера и Мейерса 35.
Спасибо за общение. Я сделал проект владельцем всех(тех которыми непосредственно управляет пользователь и которые сохраняются в файл проекта) объектов, вернее управляющим, т.к. у меня объект всегда находится в одном контейнере и для корректной работы он должен знать в каком окружении он находится то условно родителем является контейнер.
Хорошо, я(пока не найду нормального решения) отказался от идеи «идеального UNDO», анализируя популярные программы я действительно нахожу в них места где система предупреждает о том что производимые действия не имеют возможности отмены. Объекты моего проекта как правило очень небольшие и требуют очень мало ресурсов системы для поддержания их в «живом» виде. Как вы смотрите на ситуацию когда при удалении объекта сам объект физически не удаляется, а поступает как в DBF — просто устанавливает свойство Deleted в True? Свойство Deleted возвратит False если не удален сам объект и не удален его родитель(контейнер) то есть является запросом. Итераторы будут пропускать удаленные объекты и для системы они будут невидимы. При сохранении проекта удаленные объекты будут игнорированы и уже навсегда пропадут из структуры.
Есть что-то в этой схеме порочное. Как вы на это смотрите?
Сериализация — как я понял это сохранение в файл состояния объекта? Есть ли в вашем проекте паттерн Memento? Кто должен отвечать за сериализацию? У меня есть соблазн поручить это Memento в виде стратегии. Или вы считаете что этим должен заниматься отдельный класс? Просто мне кажется, и я уже писал об этом что вижу что-то общее в копировании, сериализации и хранении состояния.
Здравствуйте, byterus, Вы писали:
B>Здравствуйте, What,
B>Спасибо за общение. B>Я сделал проект владельцем всех(тех которыми непосредственно управляет пользователь и которые сохраняются в файл проекта) объектов, вернее управляющим, т.к. у меня объект всегда находится в одном контейнере и для корректной работы он должен знать в каком окружении он находится то условно родителем является контейнер.
Честно говоря, вот тут немного непонятно.
B>Хорошо, я(пока не найду нормального решения) отказался от идеи «идеального UNDO», анализируя популярные программы я действительно нахожу в них места где система предупреждает о том что производимые действия не имеют возможности отмены. Объекты моего проекта как правило очень небольшие и требуют очень мало ресурсов системы для поддержания их в «живом» виде. Как вы смотрите на ситуацию когда при удалении объекта сам объект физически не удаляется, а поступает как в DBF — просто устанавливает свойство Deleted в True? Свойство Deleted возвратит False если не удален сам объект и не удален его родитель(контейнер) то есть является запросом. Итераторы будут пропускать удаленные объекты и для системы они будут невидимы. При сохранении проекта удаленные объекты будут игнорированы и уже навсегда пропадут из структуры. B>Есть что-то в этой схеме порочное. Как вы на это смотрите?
Я думаю, что это избавит от многих проблем, связанных с тем, что объект при удалении и последующем Undo восстановится по другому адресу. Как вариант, можно вместо поля Deleted у объекта хранить за командой множество удалённых в ней объектов. Тогда:
1. При итерировании эти объекты даже не будут попадаться.
2. Если Undo не бесконечное, а, скажем, максимум на 20 команд, при удалении из очереди самой старой команды, она может "реально" удалить объекты из своего множества удалённых.
B>Сериализация — как я понял это сохранение в файл состояния объекта?
Да. Это преобразование данных в бинарный поток (при сохранении) и обратно (при загрузке). B>Есть ли в вашем проекте паттерн Memento?
Нет. Точнее, нет в той части, которую мы обсуждаемом B>Кто должен отвечать за сериализацию? У меня есть соблазн поручить это Memento в виде стратегии. Или вы считаете что этим должен заниматься отдельный класс? Просто мне кажется, и я уже писал об этом что вижу что-то общее в копировании, сериализации и хранении состояния.
Наша сериализация очень похожа на аналогичную в библиотеке MFC.
Есть объект CObj — предок всех остальных объектов, которые могут сохранятся, подчиняются Undo / Redo и т.д. Причём нет множественного наследования от него, т.е. иерархия объектов представляет собой дерево.
Каждый класс-поток CObj имеет методы
virtual void Save(CSaveArchive &) const = 0
и
virtual void Load(CLoadArchive &) = 0
для "нормального" сохранения и методы
virtual void Snapshot(CSaveArchive &) const
и
virtual void Rollback(CLoadArchive &)
для сохранения при Undo / Redo, которые по умолчанию просто вызывают Save и Load соответсвенно.
CSaveArchive и CLoadArchive — архивы записи и чтения. В методах сохранения / загрузки объект сохраняет в архивы свои поля. При сохранении ссылки на другие объекты автоматически преобразуются в идентификаторы (в качестве которых выступаются адреса памяти), а при чтении — опять в ссылки. Для обычной сериализации и сериализации внутри команды используются разные наследники классов CSaveArchive и CLoadArchive, так как по разному преобразуются ссылки <--> идентификаторы.
Таким образом за сохранение/загрузку данных отвечает сам объект, а за преобразование данных из/в бинарный поток — архивы сохранения и загрузки.
Насколько я знаю, в Delphi нет понятия "константый метод". Если это действительно так, то имеет смысл методы Save и Load объединить в один метод Serialize, а сохранение или загрузка будет определяться тем, какой архив приходит в качестве параметра. Это позволит избавиться от дублирования кода.
Что касается Memento, мне кажется, что этот pattern полезен лишь в редких случаях, например, когда надо сохранить состояние части объекта. А для сохранения состояния всего объекта, в большинстве случаев его можно просто скопировать.
W>Честно говоря, вот тут немного непонятно.
Сейчас (возможно временно т.к. это был маленький рефакторинг) проект является т.н. списком объектов, причем объекты в этот список добавляют сами контейнеры объектов в зависимости от настроек. Вот структура:
Project
Pages
TCompositeObject:Page
…
TcompositeObject
TcompositeObject
Tobject
Tobject
Как видите структура может быть довольно ветвистая, заранее неизвестная т.к. создает ее пользователь (мой проект – графический редактор), объект всегда находится только в одном контейнере. Собственно выделение объектов в список мне необходимо для быстрой проверки уникальности имени объекта (символьного идентификатора) и развертывания проекта после загрузки, когда все объекты загружены и символьные идентификаторы заменяются на ссылки.
Как я понял у вас в проекте сохранение состояния и сериализация происходит одним методом? Различия только в том что при сохранении состояния копируются реальные адреса, а при сериализации идентификаторы? Просто вчера я уже определился с тем как это будет у меня в проекте и получилось почти также как у вас. Различие в том что у меня два метода — GetState и Save. GetState выполняет всю работу, а Save просто вызывает GetState и сохраняет некоторые специфические поля.
>Насколько я знаю, в Delphi нет понятия "константый метод". Если это действительно >так, то имеет смысл методы Save и Load объединить в один метод Serialize, а >сохранение или загрузка будет определяться тем, какой архив приходит в качестве >параметра. Это позволит избавиться от дублирования кода.
Честно говоря непонятно как таким образом можно избавиться от дублирования кода?
[skip] B>Собственно выделение объектов в список мне необходимо для быстрой проверки уникальности имени объекта (символьного идентификатора) и развертывания проекта после загрузки, когда все объекты загружены и символьные идентификаторы заменяются на ссылки.
1. Зачем писать символьные идентификаторы?
2. Можно заменять идентификаторы на ссылки при чтении (см. ниже).
B>Как я понял у вас в проекте сохранение состояния и сериализация происходит одним методом? По умолчанию — да. Но объект может по-своему реализовать Snapshot и Rollback и тогда — нет. B>Различия только в том что при сохранении состояния копируются реальные адреса, а при сериализации идентификаторы?
В принципе, да. B>Просто вчера я уже определился с тем как это будет у меня в проекте и получилось почти также как у вас. Различие в том что у меня два метода — GetState и Save. GetState выполняет всю работу, а Save просто вызывает GetState и сохраняет некоторые специфические поля.
У меня в проекте сохранение/восстановления состояния — частный случай сериализации.
W>>Насколько я знаю, в Delphi нет понятия "константый метод". Если это действительно >так, то имеет смысл методы Save и Load объединить в один метод Serialize, а >сохранение или загрузка будет определяться тем, какой архив приходит в качестве >параметра. Это позволит избавиться от дублирования кода.
B>Честно говоря непонятно как таким образом можно избавиться от дублирования кода?
Вот примерно так:
// Абстрактный класс бинарного потока.
// Потоки могут объединяться в цепочку, как у GoF.class class CStream
{
public:
void Write(void const * p, size_t n) = 0;
void Read(void * p, size_t n) = 0;
};
class CArchive
{
private:
CStream * m_pStream;
public:
void DoSerialize(double &);
void DoSerialize(int &);
// ... и так для всех встроенных типов.
// реально на C++ это можно сделать шаблоном.
// Сериализация указателей.
// При чтении указателя, архив для Undo/Redo просто читает адрес,
// а обычный архив смотрит, если указатель с таким значением уже читался,
// то возвращает новый адрес, иначе создаёт объект, возвращает его адрес и заносит его в map.virtual void DoSerialize(CObj *& pObj) = 0;
};
class CSaveArchive : public CArchive;
class CLoadArchive : public CArchive;
class CSnapshotArchive : public CArchive;
class CRollbackArchive : public CArchive;
class CMyClass : public CObj
{
double m_d;
CMyClass * m_p;
// Функция чтения и записи одновременно :-)void Serialize(CArchive & Ar)
{
Ar.DoSerialize(m_d); // примитивный тип
Ar.DoSerialize(m_p); // указатель. Он при сохранении преобразуется в идентификатор.
}
};
Здравствуйте, What, Вы писали:
B>>Собственно выделение объектов в список мне необходимо для быстрой проверки уникальности имени объекта (символьного идентификатора) и развертывания проекта после загрузки, когда все объекты загружены и символьные идентификаторы заменяются на ссылки. W>1. Зачем писать символьные идентификаторы?
Дело в том что сериализация будет происходить в XML, который возможно захочет подправить (или создать вручную) пользователь. Потом это необходимо в самой логике работы с программой. Как в формах дельфийских например, ссылки на объекты являются символьными идентификаторами.
W>У меня в проекте сохранение/восстановления состояния — частный случай сериализации.
А какие еще бывают случаи? Частичное сохранение состояния?
W>>>Насколько я знаю, в Delphi нет понятия "константый метод". Если это действительно >так, то имеет смысл методы Save и Load объединить в один метод Serialize, а >сохранение или загрузка будет определяться тем, какой архив приходит в качестве >параметра. Это позволит избавиться от дублирования кода.
B>>Честно говоря непонятно как таким образом можно избавиться от дублирования кода? W>Вот примерно так:
Понял, спасибо, буду иметь в виду.
У меня проблема. При начальном проектировании я решил что передавая в конструктор объект инициализации я избавлюсь от многих проблем. Это действительно очень удобно вот смотрите:
Вот так я и сделал. Все было хорошо пока я не начал создавать фабрику Многие параметры инициализации жестко привязаны к контексту, фабрика ничего о контексте не знает. Как вы советуете поступить:
1. Создать отдельную фабрику для формирования объектов инициализации.
2. Отказатся от инициализации в конструкторе и сделать отдельный метод Init(initParams), а объект без инициализации сделать недоступным для использования. Инициализировать объект в момент попадания в контекст.
3. Сократить до минимума контекстные параметры и передавать их в главную фабрику.
Лично я склонен к 2 и 3 варианту, но дальновидность мне уже много раз изменяла, я утал от рефакторингов.
Сама фабрика является контейнером для мини фабрик, поэтому должен существовать базовый список параметров для создания объектов.
Здравствуйте, byterus, Вы писали:
W>>У меня в проекте сохранение/восстановления состояния — частный случай сериализации. B>А какие еще бывают случаи? Частичное сохранение состояния?
Ну, сохранение/загрузка проекта. Во всём проекте частичное сохранение состояния понадобилось только в одном месте — вот там и пригодился Memento
B>У меня проблема. При начальном проектировании я решил что передавая в конструктор объект инициализации я избавлюсь от многих проблем. Это действительно очень удобно вот смотрите:
B>Вот так я и сделал. Все было хорошо пока я не начал создавать фабрику Многие параметры инициализации жестко привязаны к контексту, фабрика ничего о контексте не знает. Как вы советуете поступить: B>1. Создать отдельную фабрику для формирования объектов инициализации.
Каким образом фабрика для создания объектов инициализации будет работать? Как создавать объекты инициализации (по умлочанию, из архива)? Честно говоря, мне этот вариант как-то не нравиться, инуитивно. B>2. Отказатся от инициализации в конструкторе и сделать отдельный метод Init(initParams), а объект без инициализации сделать недоступным для использования. Инициализировать объект в момент попадания в контекст.
У меня в проетке все объекты, которые могут сериализоваться, должны иметь конструктор по умолчанию (без параметров). То есть — вариант 2.
И ещё, иногда бывает очень удобно создать объект, а инициализировать чуть позже (правда, может это C++-specific). Минус такого варианта — что объект теоретически может начать использоваться не инициализированным. Я думаю, что на практике, такое бывает редко, и, ИМХО, этим минусом можно пренебречь. B>3. Сократить до минимума контекстные параметры и передавать их в главную фабрику.
Зачем сокращать, если всё равно они передаются через базовый класс TinitForBaseClass? Откуда и как взять параметры при загрузке данных?
B>Лично я склонен к 2 и 3 варианту, но дальновидность мне уже много раз изменяла, я утал от рефакторингов.
По-моему, рефакторинги — нормальный рабочий процесс. Только их лучше делать как можно раньше.
Можно к вашему списку добавить ещё один вариант, хотя я далеко не уверен, что он удобный и гибкий.
4. Создать отдельную фабрику, которая будет создавать объекты, передавая им в конструктор архив. Что-то типа такого:
class TInitForMyClass
{
};
class TMyClass : public TBaseClass
{
void Init(TInitForMyClass const &);
public:
TMyClass(TInitForMyClass const & initParams)
{
Init(initParams);
}
TMyClass(TArchive & ar)
{
TInitForMyClass initParams;
ar.Serialize(initParams); // Читаем параметры для создания
Init(initParams); // Создаём по прочитанным параметрам.
}
};
B>Сама фабрика является контейнером для мини фабрик, поэтому должен существовать базовый список параметров для создания объектов.
B>Еще раз спасибо за советы.
Я рад, если они Вам пригодились.
Здравствуйте, What, Вы писали:
W>Ну, сохранение/загрузка проекта. Во всём проекте частичное сохранение состояния понадобилось только в одном месте — вот там и пригодился Memento
То есть в командах поддерживающих отмену? Скажите, у вас в проекте Memento это что-то универсальное или параллельная ветка наследования? У меня пока Memento развивается параллельно с обслуживающими классами. У базового класса есть свойство State которое возвращает состояние, в его теле есть вызов абстрактного метода GetMemento который перекрывается в потомках и возвращает конкретный Memento. State тоже перекрывается в потомках, и Memento постепенно заполняется поднимаясь вверх по иерархии.
B>>1. Создать отдельную фабрику для формирования объектов инициализации. W>Каким образом фабрика для создания объектов инициализации будет работать? Как создавать объекты инициализации (по умлочанию, из архива)? Честно говоря, мне этот вариант как-то не нравиться, инуитивно.
Да, я тоже от него отказался. Неоправданная сложность.
B>>2. Отказатся от инициализации в конструкторе и сделать отдельный метод Init(initParams), а объект без инициализации сделать недоступным для использования. Инициализировать объект в момент попадания в контекст. W>У меня в проетке все объекты, которые могут сериализоваться, должны иметь конструктор по умолчанию (без параметров). То есть — вариант 2.
Да, я так и сделал в эти выходные, правда пока до половины – сейчас в фабрику передается информация только о том _что_ нужно создать, а внутри фабрики уже создаются пустые InitParams и передаются объектам, фактически эти InitParams объектам не нужны, но я пока их не буду убирать, скорее всего по вашему совету сделаю второй конструктор без параметров.
W>И ещё, иногда бывает очень удобно создать объект, а инициализировать чуть позже (правда, может это C++-specific). Минус такого варианта — что объект теоретически может начать использоваться не инициализированным. Я думаю, что на практике, такое бывает редко, и, ИМХО, этим минусом можно пренебречь.
Собственно у меня сейчас такая ситуация и наблюдается, я пока что не делаю проверок на инициализацию, т.к. не инициализированный объект никем не управляется и соответственно проблем нет. Сейчас я добился – сериализации/десериализации и копирования. Причем это осуществляется хоть и похожими по логике, но разными по реализации методами. Теперь очередь за сохранением состояния, и это тоже будет совсем другой метод, причем кардинально, т.к. в состоянии не будут содержаться вложенные объекты, чего я в начале никак не предполагал. При планировании я думал что все это есть один метод. В вашем случае похоже это так(кроме Memento), но я делаю сериализацию в XML, в этом imho очень большое отличие.
W>По-моему, рефакторинги — нормальный рабочий процесс. Только их лучше делать как можно раньше.
Все это так, я и стараюсь так поступать, правда сейчас у меня в самом главном приоритете это базовые классы и логика, цемент всей системы, на наследников я порой внимания не обращаю, доделаю потом когда что-то будет вырисовываться, сейчас пока «белыми нитками».
W>Можно к вашему списку добавить ещё один вариант, хотя я далеко не уверен, что он удобный и гибкий. W>4. Создать отдельную фабрику, которая будет создавать объекты, передавая им в конструктор архив. Что-то типа такого: W>
W>>Ну, сохранение/загрузка проекта. Во всём проекте частичное сохранение состояния понадобилось только в одном месте — вот там и пригодился Memento B>То есть в командах поддерживающих отмену? Скажите, у вас в проекте Memento это что-то универсальное или параллельная ветка наследования?
Единственное место, где используется Memento — в классе типа TCanvas в Delphi для сохранения матрицы преобразования.
А для сохранения состояния во время команды объекты сериализуются в специальный архив.
W>>И ещё, иногда бывает очень удобно создать объект, а инициализировать чуть позже (правда, может это C++-specific). Минус такого варианта — что объект теоретически может начать использоваться не инициализированным. Я думаю, что на практике, такое бывает редко, и, ИМХО, этим минусом можно пренебречь. B>Собственно у меня сейчас такая ситуация и наблюдается, я пока что не делаю проверок на инициализацию, т.к. не инициализированный объект никем не управляется и соответственно проблем нет. Сейчас я добился – сериализации/десериализации и копирования. Причем это осуществляется хоть и похожими по логике, но разными по реализации методами. Теперь очередь за сохранением состояния, и это тоже будет совсем другой метод, причем кардинально, т.к. в состоянии не будут содержаться вложенные объекты, чего я в начале никак не предполагал. При планировании я думал что все это есть один метод. В вашем случае похоже это так(кроме Memento), но я делаю сериализацию в XML, в этом imho очень большое отличие.
В конечном счёте, ИМХО, нет ничего плохого в том, что за сериализацию, сохранение состояния и копирование отвечают разные методы.
Здравствуйте, byterus, Вы писали:
B>Дело в том что в структуре один объект может использоваться множеством объектов, перед удалением объект посылает сообщения всем использующим его объектам, те же получив это сообщение очищают свои ссылки на удаляемый объект.
Не могли бы Вы поподробнее описать механизм сохранения списка объектов, использующих данный.
А так же механизм рассылки сообщений