Search code examples
c++design-patternsclass-designcomposite

C++: Applying the Composite pattern


I am trying to apply the Composite pattern, so I need to create a Leaf class and a Composite class, both inheriting from the same Component class. In order for any of my Components to perform their duty they need to ask help from a single Helper object. We have the following

struct Helper {

    void provide_help();
};

struct Component {

    Component(Helper* helper)
    : m_helper(helper) {
    }

    virtual void operation() = 0;

    //    the call_for_help function will be used by subclasses of Component to implement Component::operation()

    void call_for_help() {
        m_helper->provide_help();
    }

private:
    Helper* m_helper;
};

And here are two different Leaf subclasses:

struct Leaf1
: Component {

    Leaf1(Helper* helper)
    : Component(helper) {
    }

    void operation() override {
        call_for_help();
        operation1();
    }

    void operation1();
};

struct Leaf2
: Component {

    Leaf2(Helper* helper)
    : Component(helper) {
    }

    void operation() override {
        call_for_help();
        operation2();
    }

    void operation2();
};

So far, so good. Now the Composite class is giving me grief. The typical implementation is as follows

struct Composite
: Component {

    Composite(Helper* helper)
    : Component(helper) {
    }

    void operation() override {
        for (auto el : m_children) el->operation();
    }

private:
    std::vector<Component*> m_children;
};

which by going through the m_children one by one and calling operation on each essentially calls the helper function multiple times, even though one call is enough for all children. Ideally, if the m_children consisted, say, of a Leaf1 and a Leaf2, I would like somehow the Composite operation to call the helper function only once and then call in succession Leaf1::operation1() and then Leaf2::operation2(). Is there any way to achieve what I need? Alternative designs are welcome. I hope my question makes sense. Thanks in advance!


Solution

  • You want a polymorphic operation but you are adding more responability to the method (calling the helper). It's better to separate these two things.

    struct Component {
        void call_operation(){
            call_for_help();
            operation();
        }
        virtual void operation() = 0;
        void call_for_help();
    };
    

    Remove the call_for_help() from leaf::operation() (making operation1, operation2 redundant, polymorphism) and the rest should work fine.

    You can even hide operation() from your public interface, you'll need friendship with your Composite in that case.