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_children
由 Leaf1
和 Leaf2
组成,我希望 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();
重要:这个解决方案不是最优的,它有一些问题,比如你必须将一个中介实例传递给每个节点构造函数,但这只是你工作的一个原始想法上。
希望对您有所帮助!
我正在尝试应用 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_children
由 Leaf1
和 Leaf2
组成,我希望 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();
重要:这个解决方案不是最优的,它有一些问题,比如你必须将一个中介实例传递给每个节点构造函数,但这只是你工作的一个原始想法上。
希望对您有所帮助!