2015-08-14 71 views
4

我想要应用复合模式,所以我需要创建一个Leaf类和一个Composite类,它们都从相同的Component类继承。为了我的组件的任何执行他们的责任,他们需要从单个帮助对象请求帮助。我们有以下C++:应用复合模式

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

,这里是两个不同的子类叶:

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

到目前为止,一切都很好。现在复合班正在给我悲伤。典型的实现是

struct Composite 
: Component { 

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

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

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

它由一个通过一个会,并呼吁各operation基本调用辅助函数多次,即使一个电话就足够了所有的孩子。理想情况下,如果包含Leaf1Leaf2,我希望Composite操作只调用一次helper函数,然后调用Leaf1 :: operation1(),然后调用Leaf2 :: operation2()。有什么方法可以实现我需要的吗?替代设计是受欢迎的。我希望我的问题有道理。提前致谢!

+1

根据你的结构,每个孩子都可以有不同的帮手。你期望什么:每个不同的帮手应该只被称为一个?或者你确定同一个组合的所有叶子都共享同一个助手吗? – Christophe

+0

@Christophe:嗨,只有一个帮手。所以是的,我希望同一个复合材料的所有叶子共享相同的助手。 – linuxfever

+0

感谢大家的所有详细和有用的答案! – linuxfever

回答

1

你想要一个多态操作,但你的方法加入更多responability(调用帮手)。分开这两件事更好。

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

取下叶call_for_help()::操作()(使operation1,操作2冗余,多态),其余应该正常工作。

你甚至可以隐藏来自公共界面操作(),你需要在这种情况下,您的复合友谊。

+0

似乎正确和简单的方法来实现所需的东西。 – iammilind

0

因为它可能发生在任何级别,所以有一种方法可以在帮手级别处理。

的方法的素描是:

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

的原则是,呼吁由个别部件进行帮助和以前一样。但是,当复合求助电话,你把preacutions确保通话只进行一次:

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

至于说,这只是一个草图:这样提供的代码不会像你一样工作不久有几个级别的复合材料。因此,这需要改进:

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

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

  • 最后,开始/结束并不好。一个RAII助手助手可以使整个体制更加坚固(例如,当一个异常打破了正常的执行流程。)

0

我认为这个问题将通过复合和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基类:

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

重要:这个解决方案是最理想的,它有一些问题,比如你不必将调解器实例传递给每个节点的构造函数,但这只是一个原创的想法。

希望它有帮助!