这是我的第一篇文章,所以请善待。这是我最近得到的一个面试问题,但我在搜索(google、C++FAQ 等)后找不到答案。
有一个行为 b1() 的接口(interface) I1。有3个类A,B,C。所有这些类都通过覆盖 b1() 来实现接口(interface) I1。有第四个类 D,它具有在接口(interface) I1 中定义的行为 (b1) 和一个额外的行为 b2
问题是你如何设计D类。
我的答案是创建另一个接口(interface) I2,它定义行为 b2() 并通过覆盖 b1() 和 b2() 使类 D 实现 I1 和 I2(C++ 中的多重继承)
面试官同意这个解决方案,但问如果将来出现具有新行为的新类(class),我们将如何处理
我只能考虑添加更多接口(interface)(I3、I4 等)并进行多重继承,但我知道在这里您最终会得到大量具有相应接口(interface)的派生类
面试官似乎期待更好的解决方案,但他没有透露答案。我很想知道这里的专家将如何解决这个设计问题。
PS:经过深思熟虑,我认为答案可能在于使用设计模式,但是查看常见的设计模式我找不到与此问题匹配的任何模式
编辑 1:我有更多问题和说明,因此在此处编辑帖子,不确定这是否正确,或者我需要将其发布为我自己问题的答案。
首先让我感谢 @Nawaz、@Alexandre 和 @Sjoerd 的宝贵意见。我刚刚开始学习 C++/设计模式中的设计方面,所以请原谅我对这个主题的无知。
@Nawaz 的 Vistor 模式示例确实很有帮助,但我想这只是面试官提出的原始问题的一个特例。 @Alexandre 正确地指出了这里的场景。
让我解释一下这件事的另一个方面。
当我们谈论行为时,我们需要根据
1) 与一组对象或对象相关的常见行为。 (这是直观的或可以像在现实世界中一样观察到的)
例如:Dude 的行为(以@Nawaz 为例)-我走路、吃饭、学习等
2)与群体相关的不常见或非常奇特的行为(这是违反直觉的)
例如:只是为了争论,考虑一个作曲的 Dude(我知道这个例子并不完美)
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; }
};
- 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 :-) )
最佳答案
我认为面试官希望你谈论访问者模式。
是的,访问者模式允许您将新行为添加到现有的类结构中,而无需进一步向结构中添加/派生类/接口(interface)。所有这一切只需要您实现行为,而访问者模式允许您将此行为添加到类的结构中。
阅读这个维基条目;它解释了模式:
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;
}
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 ());
new 创建了许多类实例。 ,但它从未使用 delete 解除分配它们.所以你也需要考虑这个问题。int main() {
IDude *dude = new Dude();
std::vector<IDudeBehavior*> behaviours;
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;
}
关于C++ 类设计为每个不同的行为提供多个接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6965703/
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']