简体   繁体   English

C ++:应用复合模式

[英]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. 我正在尝试应用Composite模式,因此我需要创建一个Leaf类和一个Composite类,它们都继承自同一个Component类。 In order for any of my Components to perform their duty they need to ask help from a single Helper object. 为了让我的任何组件履行其职责,他们需要从单个Helper对象请求帮助。 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: 这里有两个不同的Leaf子类:

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. 通过m_children并在每个上调用operation本质上多次调用辅助函数,即使一个调用对所有子m_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(). 理想情况下,如果m_childrenLeaf1Leaf2 ,我想以某种方式复合操作只调用一次辅助函数,然后连续调用Leaf1 :: operation1()然后调用Leaf2 :: operation2()。 Is there any way to achieve what I need? 有没有办法达到我的需要? Alternative designs are welcome. 欢迎替代设计。 I hope my question makes sense. 我希望我的问题有道理。 Thanks in advance! 提前致谢!

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. 从leaf :: operation()中移除call_for_help()(使operation1,operation2冗余,多态),其余的应该可以正常工作。

You can even hide operation() from your public interface, you'll need friendship with your Composite in that case. 您甚至可以从公共界面隐藏operation(),在这种情况下,您需要与Composite建立友谊。

As it could happen at any level, one approach could be to handle this at the level of the helper. 因为它可能发生在任何级别,一种方法可能是在帮助程序级别处理此问题。

A sketch of the approach would be: 该方法的草图将是:

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

The principle is that the call for help performed by individual components works as before. 原则是由各个组件执行的求助程序的工作方式与以前一样。 But when the composite calls for help, you take preacutions to make sure that the call is performed only once: 但是当复合调用求助时,您需要进行预抽真空以确保调用仅执行一次:

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

As said, this is only a sketch: the code provided as such will not work as soon as you have several levels of composites. 如上所述,这只是一个草图:如果您有多个级别的复合材料,那么这样提供的代码将无法正常工作。 So this needs to be improved: 所以这需要改进:

  • instead of a bool composite_help you'd need a counter, which gets incremented when entering a composite operation and decremented when you exit it. 而不是bool composite_help你需要一个计数器,它在输入复合操作时递增,在退出时递减。 In this case, the counter would go back to 0 (re-enabling help) only when the last level of composte has finished its job. 在这种情况下,只有当最后一级composte完成其工作时,计数器才会返回0(重新启用帮助)。

  • may be the helper performs different operations to provide help. 可能是帮助者执行不同的操作来提供帮助。 So you could also imagine to have a "transaction id" that uniquely identifies a group of related operations, and you manage the counter not for the helper overall, in a map of active transactions. 因此,您还可以设想具有唯一标识一组相关操作的“事务ID”,并且在活动事务的映射中管理计数器而不是整体帮助程序。

  • finally, the start/end is not so nice. 最后,开始/结束并不是那么好。 A RAII helper to the helper could make the whole setup more robust (for example when an exception breaks the normal execution flow.) 帮助程序的RAII帮助程序可以使整个设置更加健壮(例如,当异常中断正常执行流程时)。

I think this problem would be better solved with a combination of Composite and Mediator . 我认为使用Composite和Mediator的组合可以更好地解决这个问题。

Heads up! 小心! I'll show you a different version of the mediator pattern, which is not the same as the canonical version. 我将向您展示不同版本的中介模式,这与规范版本不同。

It's not of the business of your composite structure to know if a helper was called or not. 知道是否调用了帮助程序并不是复合结构的业务。 You'd better do this using some kind of event handler. 你最好使用某种事件处理程序来做这件事。

Since you have only one helper, you could try like this: 由于你只有一个助手,你可以尝试这样:

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

Using it: 使用它:

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();

So, here is what you do to integrate this to you composite structure. 因此,您可以采用以下方法将其集成到复合结构中。 Remove the helper from you nodes, add the Mediator to the base class: 从您的节点中删除帮助程序,将Mediator添加到基类:

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

Using it: 使用它:

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();

IMPORTANT: This solution is suboptimal, it has some issues, like you having to pass a mediator instance to every node constructor, but it's just a raw idea for you to work on. 重要说明:此解决方案不是最理想的,它存在一些问题,例如您必须将中介实例传递给每个节点构造函数,但这对您来说只是一个原始想法。

Hope it helps! 希望能帮助到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM