我在C ++ / Qt中开发了一个图形编辑器,它使用对象树作为其数据模型。对象的类也构成了一个类层次结构。你可以在某种GUI框架中想到一个小部件树,比如Java Swing或Qt。现在我想实现一个撤消/重做功能。因此,我需要跟踪树节点的每个修改(例如,更改对象属性)以及树本身(例如,添加,删除节点)。不幸的是,树对象也将被第三方代码修改,我必须确保此代码不会破坏我的应用程序。
因此,考虑到树将是巨大的(几个100,000个节点),我如何确保,我可以看到对树的每次修改?还应该注意,树节点包含许多可修改的属性。
答案 0 :(得分:3)
Command Pattern是一种方法。但是,对数据的每次修改都需要通过Command对象完成并由撤消系统记录。这是从一开始就最好的东西。改造可能会非常痛苦。
如果您无法通过第三方代码拦截修改并将其转换为命令,则可能只需在每次更改之前拍摄所有内容的快照。如果您的数据结构很大,则存在明显的问题。您可以在撤消堆栈上安装一个特殊命令,用于仅存储此类快照以进行第三方修改。
答案 1 :(得分:0)
我没有/ Qt的经验,但是虽然这在标准c ++中相当复杂,但是有可能。
在你从字面上理解我要说的内容之前,请注意我在发布之前没有在代码中尝试这个。这应该被理解为一般概念,而不是复制粘贴(可能存在错误)
首先,您需要一个基类来表示您的节点,我们称之为Node。节点应为任何值提供mutator。
class Node
{
public: // Methods
template <typename Type>
void Set(const char* name, GenericData& attribute) const
{
// Note that GenericData now is required to have an implemented copy constructor
m_Attributes[name] = new Type(dynamic_cast<Type>(attribute));
// Note the dynamic cast to prevent slicing
// Call back some function to let it now the node changed here
}
private: // Data
// I just used a map for simplicity's sake. You should probably use
// another container more adequate to your needs.
std::map<std::string, GenericData*> m_Attributes;
}
然后我们需要一般地解决所有类型的不同类型,这也需要使用继承来完成。
class GenericData
{
public:
virtual ~GenericData() {}
}
// Example Data
template <typename T>
class Data : public GenericData
{
public:
~Data(void) { delete m_Data; }
T* get(void) const { return m_Data; }
void set(T& dat)
{
delete m_Data;
m_Data = new Data(); // Note: All data is now required to have default ctor
}
private:
T* m_Data;
}
然后客户端会输入如下内容:
Data<int> MyDat = 5; // Should be optimized to copy construction, therefore work
YourTree[TheNode].Set<int>("AttributeName", MyDat);
就像我说的,这是伪代码而不是实现,但它应该给出概念的概念。