我正在用C ++设计一个项目,并且到了我怀疑的地步,而我应该使用继承来获得多态性。
具体来说,我的课程JazzQuartet
有4个对象:Saxophonist
,Pianist
,Bassist
和Drummer
,每个对象都有play()
和listen()
方法,具有不同的实现。
我想让它们全部从类Musician
继承,因此我可以拥有一组Musician
个对象,并呼叫每个人play()
和listen()
方法,以及我可以向任何其他人Musician
listen()
。但是由于它们的实现完全不同,我只是为了获得多态性而使用这种遗产,我不确定这是否是一个很好的设计选择。
有关此问题的任何建议吗?
提前谢谢大家!
答案 0 :(得分:8)
“...所以我可以拥有一系列音乐家对象并调用每个人的play()和listen()方法,这样我就可以让任何音乐家听()给任何其他人。”
Musician
应该是一个抽象类,即接口:
class Musician {
public:
virtual void play() = 0;
virtual void listen(Musician& other) = 0;
virtual bool isPlaying() = 0;
virtual ~Musician() {}
};
是的,它被认为是推广界面的好设计。
这样,您可以强制派生类必须实现这些功能,并允许客户端访问Musician
个实例,而无需了解具体的派生类型。
正如您一直要求将整个集合存储到数组中一样:
通过上述设计,您可以使用std::unique_ptr<Musician>
数组来聚合特定的音乐家群。
std::vector<std::unique_ptr<Musician>> jazzQuartet(4);
std::unique_ptr<Saxophonist> sax = new Saxophonist();
std::unique_ptr<Pianist> piano = new Pianist();
std::unique_ptr<Bassist> bass = new Bassist();
std::unique_ptr<Drummer> drums = new Drummer();
jazzQuartet[0] = sax;
jazzQuartet[1] = piano;
jazzQuartet[2] = bass;
jazzQuartet[3] = drums;
// And wire them up as necessary
//------------------------------------
// Usually everyone in the combo needs to listen to the drums
sax->listen(*drums);
piano->listen(*drums);
bass->listen(*drums);
...
// Let them all play
for(auto& m : jazzQuartet) { // Note the & to avoid copies made for the items
m->play();
}
答案 1 :(得分:7)
我认为没有理由担心您的Musician
实现不会有任何公共代码。实际上,这正是所谓的Pure Abstract Class。一般来说,这是分离界面概念的好主意。
它为您提供了比您提到的更多的优势,最重要的是您很可能会发现您的其他代码不需要知道它正在使用的Musician
特定类型,因此您的主要代码会更简单。
这也不是“只是”多态,它也会促使封装很多,因为用户将被迫使用Musician
接口。
另外,我认为将来你可能会发现你实际上需要不同音乐家之间的一些公共代码(比如导演/指挥对象参考?)。
答案 2 :(得分:5)
这是多态性的完全合理用法。 Saxophonist
,Pianist
,Bassist
和Drummer
都与Musician
展示了“Is-a”关系。
您的Musician
类将是纯虚拟的(也称为接口)。
答案 3 :(得分:2)
只是为了支持diavolo,
你也可以使用成分 - 例如你有单班音乐家和2名代表,非虚拟方法 - 听和玩。那么你应该再有4个claeses,每个都为每种类型的音乐家。然后在Musician构造函数中,您将提供这四个分类的类。
但很大但是 - 你再一次需要基础课。组合/委派/策略“模式”的优势是双重的,至少在这种情况下是这样。
这仅表示您当前的方法是合理的。继承继承:)
答案 4 :(得分:0)
应用程序增长是这里的主要因素。除非这仅用于学术实践(在这种情况下它将具有教育价值),否则如果申请没有增长则不会。我们必须要小心的一件事是永远不要过度设计解决方案。我们的解决方案应始终是最简单的解决业务需求的解决方案。为什么?仅仅因为,我们无法预测未来,我们需要确保我们的努力朝着为客户提供价值并因此为我们自己提供价值的解决方案。否则,如果您的应用程序正在增长并且当前的设计正在阻碍(可能是您现在所处的位置),我们需要增强设计。要回答您的问题,多态性可以在以下方面增强您的设计:
首先,由于所有音乐家都会实现 Musician 界面,所以他们都能够以多态方式播放和收听,即你可以循环播放你的阵列。这是多态性的主要好处。
其次,它将使代码重用和增强应用程序更容易。例如,将来可以轻松添加音乐家。所需要的只是看一下界面,了解音乐家需要实现的方法。在代码重用方面,在较大的应用程序中,您可以在管道应用程序上重用大部分代码。
第三,在未来的迭代中,重构变得更容易。例如,假设您需要在钢琴家上重新实现play()方法,在JazzQuartet上不需要更改任何其他内容,而如果您保留原样,则可能需要。怎么样?实现到接口也称为按合同设计,因为它强制子类实现接口上指定的方法。您对现有设计没有这种限制,因此没有什么可以阻止您或其他提交者更改播放方法的名称,以说PianistPlay将更改JazzQuartet是必要的。
第四,它提高了可维护性。维护代码的人肯定会更清楚这四个对象是相关的并且实现相同的接口,而不是它们保持四个不同的对象。在大多数情况下,查看界面可能足以确定如何使用或增强对象。另一方面,简单地使用对象将需要查看每个实现,这取决于应用程序的增长程度可能相当繁琐。在尝试增强唯一对象时,事情可能变得非常复杂。如果你正在使用一个接口而你想要添加一个新方法,你将不得不去每个类并实现它,但想象一下如果没有接口就尝试做同样的事情,如果使用良好的命名约定并且非常相似如果不是,则单调乏味,即所有play方法都包含类的名称,例如bassistPlay()。答案 5 :(得分:0)
您可以使用Duck Typing:
A if condition else B
这消除了对任何继承或(公共)(虚拟)接口的需要。基本模型更复杂,但模型的实现是微不足道的。
答案 6 :(得分:-1)
您可能会考虑类型擦除模式。 Sean Parent一直在倡导使用值类型,并且他经常使用类型擦除来使用没有共同基础的类型。开销与使用接口基类相同,但如果不在擦除上下文中使用类型,则没有开销。