从复合图案结构中删除元素

时间:2012-09-11 08:43:39

标签: c++ algorithm oop design-patterns inheritance

假设我们有最简单的结构,类ComponentLeaf : ComponentComposite : ComponentLeaf类的每个对象都有一个int id,它赋予它身份。复合类会像:

class Composite : public Component
{
public:
    void removeComponent(Component*);
// other stuff
private:
    std::vector<Component*> coll;
};

和叶班一样:

class Leaf : public Component
{
public:
    //stuff
    int getID();
private:
    int id;
};

问题是如何定义函数removeComponent(Component* cmp)。 cmp实际上是Leaf,但我需要访问Component向量coll,所以它需要是Component(我认为)。 removeComponent方法接受一个Leaf对象,并从整个结构中删除具有相同ID的所有其他叶子。

现在,我想到了两种方式(两者都不起作用:P):

第一

void Composide::removeComponent(Component* cmp)
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        (*it)->removeComponent(cmp);
    // where removeComponent is defined as virtual in Component, but
    // the problem is that Leaf cannot erase itself from the collection
    }

}

第二

void Composide::removeComponent(Component* cmp)
    {
        std::vector<Component*>::const_iterator it;
        for(it = coll.begin(); it != coll.end(); ++it)
        {
            if((*it)->equals(*cmp))
            it = erase(it);
        // But here I can't define equals function for Composite elements,
    // so then I'd have to make functions which return the type of Component,
    // and in case of Composite call recursively the function and
    // in the case of Leaf call the equals() function and erase if needed.
    // This however looks like really bad OOP practice, and I'd like to avoid
    // using those methods which return type..
        }

    }

必须有一个简洁,好的方法来做到这一点。我认为该方法应该看起来像上面提到的第一种方式,只是我真的不知道如何使Leaf从向量中删除自己。请帮帮我? :)

2 个答案:

答案 0 :(得分:3)

你似乎对Component究竟是什么感到困惑。它是某个业务对象,还是表示树节点的类?如果它是树节点,则所有树节点都应支持相同的操作以允许轻松递归。

因此,我会将removeComponent()的定义移到基础Component类并将其设为虚拟。您可以在Component中提供空实现。

您的复合实现就是:

void Composide::removeComponent(Component* cmp)
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        if((*it)->equals(*cmp))
           it = erase(it);
        else
           (*it)->removeComponent(cmp);
    }
}

编辑:关于Id的

同样,我认为你可能会混淆两个概念,即组件ID和组件。 (如果将Component重命名为TreeItem可能会更好吗?)

您当前的removeComponent()函数会使用Component指针,从而推断可以从树中删除任何Component(包括Composites)。这对我来说似乎是正确的。您可能需要删除Composites。因此,您可以简单地比较指针。

然而,您似乎正在比较只有Leafs的Id(通过假设的相等重载)。

如果你想提供一个额外的函数来删除Id,那么我也会将GetID()函数移动到基类Component类,并与之进行比较。 Composite个对象可以返回-1或其他一些空标记。

例如

void Composite::getID()
{
   return -1;
}

void Composide::removeComponent(Component* cmp)
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        if((*it) == cmp)
           it = erase(it);
        else
           (*it)->removeComponent(cmp);
    }
}

void Composite::removeComponentById(int id)
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        if((*it)->getID() == id)
           it = erase(it);
        else
           (*it)->removeComponentById(id);
    }
}

答案 1 :(得分:-1)

我会选择这种方法:

void Composide::removeComponent(Component* cmp)
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        if((*it)->equals(*cmp))
        {
            delete *it;
            coll.erase(it);
            return;//can there be more than one?
        }
        else
        {
            //iterative call
            it->removeComponent(*cmp);
        }
    }
}

void Composide::~Composite()
{
    std::vector<Component*>::const_iterator it;
    for(it = coll.begin(); it != coll.end(); ++it)
    {
        delete *it;
        it = coll.erase(it);
    }
}

如果删除了Composite,则Composite的析构函数应该处理自身的所有子组件。

通常我会建议使用智能指针。不再需要删除了。

equals(..)方法不应该是Component中的纯虚方法,而是在每个子节点中实现。

编辑:添加if / else进行迭代搜索