我在GUI设计中遇到了一个OOP问题,但让我以动物为例进行说明。让我们进行以下设置:
对于动物has-a牙齿来说这是很自然的,但是现在我需要诸如has-a关系的接口。例如,如果我有一个动物矢量,那么我如何制作每个Bite()?
std::vector<Animal *> animals;
animals.push_back(new dog());
animals.push_back(new fly());
animals.push_back(new cat());
void Unleash_the_hounds(std::vector<Animal *> animals)
{
//bite if you can!
}
我想出了几种解决方案,但似乎没有一种非常合适:
1.)每个带有牙齿的类也实现接口IBiting。但是,此解决方案引入了很多代码重复,我将需要在每个类中“实现” Bite():
class Cat : public Animal, public IBiting {
Teeth teeth;
public:
virtual void Bite() { teeth.Bite(); }
}
2.)给每只动物牙齿,但只允许一些使用它们。注意:语法可能是错误的-仅用于说明
class Animal{
static cosnt bool canBite = false;
Teeth teeth;
public:
void Bite() { this->canBite ? teeth.Bite() : return; }
}
class Cat {
static cosnt bool canBite = true;
}
3.)更多继承-创建类BitingAnimal并派生它。好吧,这可能行得通,但是如果我需要派生(非)飞行动物,其中一些有牙齿,该怎么办?
class Animal{}
class BitingAnimal : public Animal {
Teeth teeth;
}
并用作BitingAnimal.teeth.Bite()
4.)多重继承。通常不鼓励这样做,并且在大多数语言中是不可能做到的,此外,让Cat成为牙齿也不合逻辑。
class Cat : public Animal, public Teeth {
}
5.)可以咬的类的枚举-仅凭其声音感到奇怪。
还是我只是过于复杂而错过了重要的事情?
1)界面不错,您可以通过以下方式添加默认实现:
class IBiting { public virtual void bite() = 0 };
class HasTeeth, public IBiting { Teeth teeth; public:
virtual void bite() override { teeth.bite(); } };
for(Animal* a: animals) {
IBiting* it = dynamic_cast<IBiting*>(a);
if(it) it->bite(); }
1b)...您也可以完全删除该界面并仅使用HasTeeth
:
class HasTeeth { Teeth teeth; public:
void bite() { teeth.bite(); } };
for(Animal* a: animals) {
HasTeeth* it = dynamic_cast<HasTeeth*>(a);
if(it) it->bite(); }
2)Animal
如果您不想使用RTTI /,可以使用Bloating dynamic_cast
。您可以virtual void bite()
在Animal上使用空实现,然后稍后将其覆盖(添加Teeth
)。如果您坚持不使用RTTI,则不需要那么多的编码,但是如果可以使用dynamic_cast
,为什么不使用它呢?
编辑:沃恩·卡托(Vaughn Cato)的答案非常适合此-虚拟/抽象teethPtr()
(或getTeeth()
)在Animal中具有类似的快捷方式biteIfYouCan()
。对嵌入式世界(微芯片)有利,但对PC来说,我还是更喜欢dynamic_cast
。
3)虚拟继承可以帮助我们解决BitingAnimal
vs FlyingAnimal
:
class BitingAnimal: public virtual Animal {
Teeth teeth; public void bite() { teeth.bite(); } };
class FlyingAnimal: public virtual Animal {
Wings wings; public void fly() { wings.fly(); } };
class FlyingBitingAnimal: /*public virtual Animal, */
public FlyingAnimal, public BitingAnimal {};
4)加入Animal
并Teeth
没有感觉,除非你完全删除Teeth
,取而代之的是HasTeeth
或CanBite
。然后变成我的1b。
5)该枚举是2型腹胀动物的另一个版本。不好。但这使我想到了不使用dynamic_cast的替代方法:您可以通过可以告诉您的功能(枚举或动物标志bool can_bite
)来模仿它,这种转换是安全的。然后,您可以使用多重/虚拟继承来模拟dynamic_cast(首先检查功能,然后投射)。
编辑: 沃恩·卡托(Vaughn Cato)也teethPtr()
与此匹配(向我展示如果您有牙齿可以咬的牙齿),并且不需要石膏。
回答评论:
简而言之:尝试命名功能(能力,做某事的能力,提供某事的能力)。
长答案:您需要Teeth
和HasTeeth
还是单身CanBite
?您的第四个解决方案在原则上还不错,但是在命名以及其他可能性上也不错。所有这些都是假设的。接口在其他语言中(单继承+接口)HasTeeth
是众所周知的,类似于C#IListSource
和IList GetList()
andbool ContainsList
(用于擦除),其中bite()
不直接存在,但可以通过扩展方法添加:
static IEnumerator GetEnumerator(this IListSource it) {
if(!it.ContainsList) yield break;
foreach(object o in it.GetList()) yield return o; }
在这里您可以看到,使用C#扩展方法与使用C ++多重继承可以达到相同的目的。该名称是-一个形式- SourceOf。您可以与我们共享您的GUI的真实姓名吗?
来自C ++的示例是iostream = istream + ostream,具有从ios的虚拟继承。再次是-命名。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句