C++:应用复合模式

C++: Applying the Composite pattern

我正在尝试应用 Composite 模式,因此我需要创建一个 Leaf class 和一个 Composite class,它们都继承自相同的 Component class。为了让我的 any 组件履行职责,他们需要向单个助手 object 寻求帮助。我们有以下

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;
};

这里有两个不同的 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();
};

到目前为止,还不错。现在 Composite class 让我很伤心。典型实现如下

struct Composite
: Component {

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

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

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

通过一个一个地检查 m_children 并在每个上调用 operation 本质上多次调用辅助函数,即使一次调用就足以满足所有 children。理想情况下,如果 m_childrenLeaf1Leaf2 组成,我希望 Composite 操作仅调用一次辅助函数,然后连续调用 Leaf1::operation1() 然后 Leaf2::operation2()。有什么办法可以达到我的需要吗?欢迎替代设计。我希望我的问题是有道理的。提前致谢!

由于它可能发生在任何级别,因此一种方法是在助手级别处理此问题。

该方法的草图是:

class Helper {
    bool composite_help = false;
    bool help_provided;  
public:     
    void provide_help() { 
       if ((composite_help && !help_provided) || !composite_help) {
           //TO DO: provide help
           help_provided = true;
       }
     }   
    void start_composite_help() {
       composite_help = true; 
       help_provided = false;
    } 
    void end_composite_help() {
       composite_help = false; 
    } 
};

原则是各个组件执行的求助功能和以前一样。但是当组合请求帮助时,您采取预防措施以确保只执行一次调用:

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

如前所述,这只是一个草图:一旦您有多个级别的复合材料,这样提供的代码将无法正常工作。所以这里需要改进:

  • 而不是 bool composite_help 你需要一个计数器,它在进入复合操作时递增,在退出复合操作时递减。在这种情况下,只有当最后一层堆肥完成其工作时,计数器才会回到 0(重新启用帮助)。

  • 可能是帮助者执行不同的操作来提供帮助。所以你也可以想象有一个 "transaction id" 来唯一标识一组相关操作,并且你在活动事务的映射中管理计数器而不是为助手整体。

  • 最后,start/end不太好看。助手的 RAII 助手可以使整个设置更加健壮(例如,当异常中断正常执行流程时。)

您想要一个多态操作,但您正在为方法添加更多的责任(调用助手)。把这两个东西分开比较好。

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

从 leaf::operation() 中删除 call_for_help()(使操作 1、操作 2 冗余、多态),其余的应该可以正常工作。

您甚至可以在 public 界面中隐藏 operation() ,在这种情况下,您需要与 Composite 建立友谊。

我认为结合 Composite 和 Mediator 可以更好地解决这个问题。

注意!我将向您展示一个不同版本的中介模式,它与规范版本不同。

知道是否调用了助手与您的复合结构无关。您最好使用某种事件处理程序来执行此操作。

因为你只有一个帮手,你可以这样试试:

class Helper {
    public:
        void callHelper() { std::cout << "Helper called" << std::endl; }
};

class Mediator {
    private:
        std::map<std::string, std::vector<Helper>> subscribers;
        int updateLimit = -1;
        int currentUpdateCount = 0;

        void resetUpdateCount() {
            currentUpdateCount = 0;
        }
    public:
        Mediator(){}

        void subscribe(std::string evt, Helper helper) {
            subscribers[evt].push_back(helper);
        }

        void update(std::string evt) {
            for (auto& h: subscribers[evt]) {
                h.callHelper();
            }
        }

        void setUpdateLimit(int i) {
            updateLimit = i;
            resetUpdateCount();
        }

        void removeUpdateLimit() {
            updateLimit = -1;
            resetUpdateCount();
        }

        int getUpdateLimit() {
            return updateLimit;
        }


        void updateLimited(std::string evt) {
            if (updateLimit < 0 || currentUpdateCount < updateLimit) {
                update(evt);
                currentUpdateCount++;
            } 
        }
};

int main(int argc, const char *argv[])
{

    Mediator m;
    Helper h1, h2;
    m.subscribe("bar", h1);


    m.setUpdateLimit(1);
    // Will be called only once
    m.updateLimited("bar");
    m.updateLimited("bar");
    m.updateLimited("bar");
    m.removeUpdateLimit();

    return 0;
}

使用它:

Mediator m;
Helper h1, h2;
m.subscribe("bar", h1);


m.setUpdateLimit(1);
// Will be called only once
m.updateLimited("bar");
m.updateLimited("bar");
m.updateLimited("bar");
m.removeUpdateLimit();

所以,这就是将其集成到复合结构中的方法。从你的节点中删除助手,将 Mediator 添加到基础 class:

struct Component {

    Component(Mediator& mediator)
    : m_helper(mediator) {
    }

    virtual void operation() = 0;

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

    void notify() {
        m_mediator->updateFiltered(Component::updateEventName);
    }
    static std::string updateEventName;
private:
    Mediator& m_mediator;
};

std::string Component::updateEventName = "update.composite";

struct Leaf1
: Component {

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

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

    void operation1();
};

使用它:

Mediator m;
Helper h;

Composite c(m);
Leaf1 l1(m), l2(m);
c.add(l1);
c.add(l2);

m.subscribe(Component::updateEventName, h);

m.setUpdateLimit(1);
// Will be called only once, even if it has childrens
c.update();
m.removeUpdateLimit();

重要:这个解决方案不是最优的,它有一些问题,比如你必须将一个中介实例传递给每个节点构造函数,但这只是你工作的一个原始想法上。

希望对您有所帮助!