我有一些我想上课的行为。我想隔离这些行为,以便我可以重用该代码,随意混合和匹配。
例如,一种方法是:
class BehaviorAbstract {
protected:
virtual void processInfo(Info i) = 0;
}
class Behavior1: public BehaviorAbstract {
protected:
virtual void processInfo(Info i) { ... }
void performBehavior1() { ... }
}
class Behavior2: public BehaviorAbstract {
protected:
virtual void processInfo(Info i) { ... }
void performBehavior2() { ... }
}
class ConcreteObject: public Behavior1, Behavior2 {
protected:
void processInfo(Info i) {
// needs to call processInfo of Behavior1 and Behavior2
Behavior1::processInfo(i);
Behavior2::processInfo(i);
}
void perform() {
this->performBehavior1(); this->performBehavior2();
}
}
这就是问题的症结所在:ConcreteObject
需要调用其继承的所有类的2个函数processInfo
(名称相同,参数相同)。想象一下,所有行为类都是由不同的开发人员编码的。该函数具有相同的名称,因为它们都源自BehaviorAbstract。
执行此操作的合理设计模式是什么?我怀疑多重继承在这里可能是错误的,也许“多重组合”会更好,但是我需要所有Behavior
类和ConcreteObject
从BehaviorAbstract
派生出来,它们都需要在BehaviorAbstract
的同一受保护数据成员上进行操作。
我在上面写的解决方案让人感到错误和丑陋。有没有一种方法可以自动调用所有实现processInfo的父类,而不必显式地重写它们的名称?
非常感谢您的帮助。
答案 0 :(得分:1)
如果我说对了,那么这个问题就是关于重构ConcreteObject
类的。
方法1:
如果您可以使performBehavior()
成为BehaviorAbstract
基类的一部分,那么您可以简单地使用BehaviorAbstract*
的向量并让多态性来做。我认为这可以视为策略模式。
#include <iostream>
#include <vector>
typedef int Info;
struct BehaviorAbstract
{
virtual void processInfo(Info i) = 0;
virtual void performBehavior() = 0;
};
struct Behavior1 : BehaviorAbstract
{
void processInfo(Info i) override
{ std::cout<< "Behavior1::processInfo()" <<std::endl; }
void performBehavior() override
{ std::cout<< "Behavior1::performBehavior()" <<std::endl; }
};
struct Behavior2 : BehaviorAbstract
{
void processInfo(Info i) override
{ std::cout<< "Behavior2::processInfo()" <<std::endl; }
void performBehavior() override
{ std::cout<< "Behavior2::performBehavior()" <<std::endl; }
};
//------------------------------------------------//
struct ConcreteObject
{
typedef std::vector<BehaviorAbstract*> vec_behavior;
vec_behavior vba;
ConcreteObject(vec_behavior &&v) : vba(v)
{;}
void processInfo(Info i)
{
for (auto &&itr : vba)
itr->processInfo(i);
}
void perform()
{
for (auto &&itr : vba)
itr->performBehavior();
}
};
int main()
{
ConcreteObject foo = {{new Behavior1(), new Behavior2()}};
foo.processInfo(23);
foo.perform();
}
示例:https://rextester.com/UXR42210
方法2:
使用创建元组的可变参数模板。遍历该元组并运行功能。同样,如果performBehavior1()
和performBehavior2()
可以共享相同的函数名称,那么它将变得更加容易。这里额外的复杂性是您需要编写手动遍历该元组的方式。为简单起见,我直接从processInfo()
结构中调用了iterate_tuple
。
#include <iostream>
#include <tuple>
typedef int Info;
struct BehaviorAbstract
{
virtual void processInfo(Info i) = 0;
};
struct Behavior1 : BehaviorAbstract
{
void processInfo(Info i) override
{ std::cout<< "Behavior1::processInfo()" <<std::endl; }
void performBehavior1()
{ std::cout<< "Behavior1::performBehavior1()" <<std::endl; }
};
struct Behavior2 : BehaviorAbstract
{
void processInfo(Info i) override
{ std::cout<< "Behavior2::processInfo()" <<std::endl; }
void performBehavior2()
{ std::cout<< "Behavior2::performBehavior2()" <<std::endl; }
};
//------------------------------------------------//
template<typename T, std::size_t N>
struct iterate_tuple
{
static void run(T &t, Info i)
{
std::get<N>(t).processInfo(i);
iterate_tuple<T, N-1>::run(t,i);
}
};
template<typename T>
struct iterate_tuple<T, 0>
{
static void run(T &t, Info i)
{
std::get<0>(t).processInfo(i);
}
};
//------------------------------------------------//
template<typename ...T>
struct ConcreteObject
{
std::tuple<T ...> tmp;
static constexpr std::size_t tuple_size = std::tuple_size<decltype(tmp)>::value;
ConcreteObject() : tmp{std::forward<T>(T()) ...}
{;}
void processInfo(Info i)
{
iterate_tuple<decltype(tmp), tuple_size-1>::run(tmp, i);
}
void perform()
{
std::get<0>(tmp).performBehavior1();
std::get<1>(tmp).performBehavior2();
}
};
int main()
{
ConcreteObject<Behavior1,Behavior2> foo;
foo.processInfo(23);
foo.perform();
}
示例:https://rextester.com/SBRE16218
这两种方法都避免了多重继承,据我所知,这是您要避免的。仅供参考,越简单越好。