这是我的第一篇文章,所以要善良.这是我最近得到的一个面试问题,但我在搜索后找不到答案(谷歌,C++ FAQ等).
存在具有行为b1()的接口I1.有A,B,C三个等级.所有这些类都通过覆盖b1()来实现接口I1.存在第四类D,其具有在接口I1中定义的行为(b1)和额外行为b2
问题是你如何设计课程D.
我的答案是创建另一个接口I2,它定义行为b2()并使类D通过覆盖b1()和b2()实现I1和I2(C++中的多重继承)
采访者同意解决方案,但询问如果将来有新的行为出现的新课程,我们将如何处理
我只能考虑添加更多的接口(I3,I4等)并进行多重继承,但我知道你最终会在大量具有相应接口的派生类中
面试官似乎期待更好的解决方案,但他没有透露答案.我很想知道这里的专家如何解决这个设计问题.
PS:经过认真思考后,我认为答案可能在于使用设计模式,但看看常见的设计模式,我找不到任何与此问题相符的模式
编辑1:我有更多的问题和澄清,所以在这里编辑帖子,不确定这是否正确或我需要发布这个作为我自己的问题的答案.
首先让我感谢@Nawaz,@ Alexandre和@Sjoerd的宝贵意见.我刚刚开始学习C++ /设计模式中的设计方面,所以请原谅我对这个主题的无知.
@Nawaz的Vistor模式的例子非常有用,但我想这只是访问者提出的原始问题的一个特例.@Alexandre正确地指出了这里的场景.
让我解释一下这个的另一个方面.当我们谈论行为时,我们需要基于它们对它们进行分组
1)与一组对象或对象相关的常见行为.(这是直观的,或者可以在现实世界中观察到)例如:Dude的行为(以@Nawaz为例) - 我,走路,吃饭,学习等
2)与小组相关的不常见或非常特殊的行为(这是反直觉的)例如:仅仅为了争论,考虑一个组成音乐的家伙(我知道这个例子不完美)
3)与小组完全无关的行为.我想不出一个例子,但我的观点是让我们说出一些奇怪的原因我们需要给对象这个行为.
所以我认为访客模式可以解决1)中的问题,但我怀疑它不适用于2)和3).
以IDude为例,我们需要进行以下更改才能成为可以创作音乐的Dude.
class IComposerBehavior; class IComposer { public: virtual ~IComposer() {} virtual void accept(IComposerBehavior *behaviour) = 0 ; }; class IComposerBehavior { public: virtual ~IComposerBehavior() {} virtual void visit(IComposer * ) = 0; }; class Dude : public IDude, public IComposer { public: virtual void accept(IDudeBehavior *behaviour) { behaviour->visit(this); } virtual void accept(IComposerBehavior *behaviour) { behaviour->visit(this); } }; class SymphonyComposerBehavior : public IComposerBehavior { public: virtual void visit(IComposer *dude) { cout << "Dude is composing a symphony" << endl; } };
同样,我们还需要更改客户端代码以考虑SymphonyComposerBehavior.
所以基本上我们最终改变了Dude类代码和客户端代码,否定了模式的影响.
我认为面试官询问的是新行为,这些新行为不能归入以前确定的一组相关行为中.所以在这种情况下,即使类被修复,访问者模式也可以解决,如@Alexandre指出的那样?
让我举一个例子来说明我的头脑(不确定这是否是一个正确的例子来表示问题).让我们说我需要为机器人制造公司设计一个应用程序.要求逐渐增长
- Initially We are only producing Toy Robots - Then Human helper Robots - Then Self Healing Robots (would just correct itself when defective) - Then Humanoid Robots - Then machine Robots (that are not human like but as a substitute for any machine you can think of) . I have deliberately put this here even though its place should be before with a correct evolution scheme. - finally Humanoid Robots with life (atleast we can dream :-) )
因此,如果我们在设计应用程序之前知道机器人的完整列表,我们可以提供更好的设计,但是我们如何设计何时按上述顺序按顺序引入每种新类型.我的观点是,我们知道机器人具有某些行为或特征,但是当以后必须引入不常见的特征(如自我修复,机器人机器人)时,我们该怎么办?
谢谢.
我认为面试官期待你谈论访客模式.
是的,访问者模式允许您向现有的类结构添加新行为,而无需进一步添加/派生结构的类/接口.所有它只需要您实现行为,访问者模式允许您将此行为添加到类的结构中.
阅读此维基条目; 它解释了模式:
访客模式
以下是访问者模式的一个简单实现:
class IDudeBehavior; class IDude { public: virtual ~IDude() {} virtual void accept(IDudeBehavior *behaviour) = 0 ; }; class IDudeBehavior { public: virtual ~IDudeBehavior() {} virtual void visit(IDude * ) = 0; }; class Dude : public IDude { public: virtual void accept(IDudeBehavior *behaviour) { behaviour->visit(this); } }; class LaughDudeBehavior : public IDudeBehavior { public: virtual void visit(IDude *dude) { cout << "Dude is Laughing" << endl; } }; class WalkDudeBehavior : public IDudeBehavior { public: virtual void visit(IDude *dude) { cout << "Dude is Walking" << endl; } }; int main() { IDude *dude = new Dude(); dude->accept(new LaughDudeBehavior()); dude->accept(new WalkDudeBehavior()); return 0; }
在线演示:http://ideone.com/Kqqdt
截至目前,该类Dude
只有两种行为,即LaughDudeBehavior
,WalkDudeBehavior
但由于它是一个访问者模式,您可以添加任意数量的行为Dude
,而无需编辑该类Dude
.例如,如果要添加EatDudeBehavior
和StudyCplusCplusDudeBehavior
,那么您需要实现的所有内容IDudeBehavior
:
class EatDudeBehavior : public IDudeBehavior { public: virtual void visit(IDude *dude) { cout << "Dude is Eating" << endl; } }; class StudyCplusCplusDudeBehavior : public IDudeBehavior { public: virtual void visit(IDude *dude) { cout << "Dude is Studying C++" << endl; } };
然后你需要接受这些行为:
dude->accept(new EatDudeBehavior ()); dude->accept(new StudyCplusCplusDudeBehavior ());
添加这些新行为后的演示:http://ideone.com/9jdEv
避免内存泄漏
上述代码存在一个问题.一切看起来都很好,除了泄漏记忆.该程序使用创建许多类的实例new
,但它从未使用它们解除分配delete
.所以你也需要考虑这个问题.
内存泄漏可以很容易地修复为:
int main() { IDude *dude = new Dude(); std::vectorbehaviours; behaviours.push_back(new LaughDudeBehavior()); behaviours.push_back(new WalkDudeBehavior()); behaviours.push_back(new EatDudeBehavior()); behaviours.push_back(new StudyCplusCplusDudeBehavior()); for(size_t i = 0 ; i < behaviours.size() ; i++ ) dude->accept(behaviours[i]); //deallcation of memory! for(size_t i = 0 ; i < behaviours.size() ; i++ ) delete behaviours[i]; delete dude; return 0; }
现在没有内存泄漏.:-)