我有两个并行的继承链:
Chain1:
Animal <- Lion
<- Gazelle
Chain2:
Food <- Meat
<- Grass
我想在Animal上实现“Eats”多态属性。这就是它的样子:
public abstract class Animal
{
public abstract Food Eats { get; set;}
}
public class Lion : Animal
{
public override Food Eats
{
get { return new Meat();}
set
{
if (value is Meat) DoSomething(value);
else throw new Exception("Lions only eat meat. " +
"You better learn that, dude!");
}
}
}
但是,此代码不是类型安全的。如果我用狮子喂狮子,我只会在运行时面对我的虫子。
有人能为我提供一个代码示例,在不牺牲多态性的情况下使用泛型来促进类型安全吗?
答案 0 :(得分:3)
使用组合而不是继承:
不是基于消化系统继承,而是将消化分解为自己的一类 首先,描述不同吃法的界面。
public interface IDigest
{
void Eat(Meat food);
void Eat(Plant food);
void Eat(Offal food); //lol nethack
}
食肉动物吃肉,有时可以吃草药,不喜欢废话:
public class Carnivorous : IDigest
{
public void Eat(Meat food)
{
Console.Write("NOM NOM");
}
public void Eat(Plant food)
{
if(Starving)
Console.Write("Ugh, better than nothing.");
else
Vomit();
}
public void Eat(Offal food)
{
Vomit();
}
}
草食动物很挑剔,宁愿死也不愿吃肉(我知道,保存你的评论,这是一个例子)
public class Herbivorous : IDigest
{
public void Eat(Meat food)
{
Vomit();
}
public void Eat(Plant food)
{
Console.Write("NOM NOM");
}
public void Eat(Offal food)
{
Vomit();
}
}
杂食动物吃任何东西。见证一个州公平。
public class Omnivorous : IDigest
{
public void Eat(Meat food)
{
Console.Write("NOM NOM");
}
public void Eat(Plant food)
{
Console.Write("NOM NOM");
}
public void Eat(Offal food)
{
Console.Write("NOM NOM");
}
}
所有动物必须进食,因此必须有消化系统和其他系统。
public abstract class Animal
{
/* lots of other stuff */
public IConsume DigestiveSystem {get;set;}
/* lots of other stuff */
}
嬉皮士是一种具有已知口味的动物类;它在实例化时配置自己。也可以从外部注入行为和系统。
public class Hippie : Animal
{
public Hippie()
{
/*stuff*/
DigestiveSystem = new Herbivore();
BodyOdorSystem = new Patchouli();
/*more stuff*/
}
}
最后,让我们看一个嬉皮士吃汉堡。
public static void Main()
{
Hippie dippie = new Hippie();
Meat burger = new Meat("Burger", 2/*lb*/);
dippie.DigestiveSystem.Eat(burger);
}
在对像动物这样的复杂系统进行建模时,我更喜欢任意组合而不是继承。复杂系统可以快速爆炸继承树。采取三种动物系统:杂食动物/食草动物/食肉动物,水/空气/陆地,以及夜间/昼夜。让我们甚至不担心如何决定哪种分类成为动物的第一个分化点。我们首先将Animal扩展到Carnivore,首先扩展到WaterLiving,还是先将Nocturnal扩展?
由于杂食动物可以生活在空中并且喜欢夜晚(蝙蝠*)并且也是一天行走的陆地生物(人类),你必须拥有一个可以击中每一个选项的继承路径。这是一个有54种不同类型的继承树(早期的,善良的)。动物比这复杂得多。您可以轻松获得具有数百万种类型的继承树。遗传的构成,绝对是。
例如,*新西兰短尾蝙蝠是杂食性的。答案 1 :(得分:1)
动物可以是通用类:
public abstract class Animal<T> where T : Food
{
public abstract T Eats {get;set;}
}
那么你可以让狮子成为像这样吃肉的动物
public class Lion : Animal<Meat>
{
//etc...
}
但这不是最佳解决方案。您不能再将动物用作多态接口,因为您需要了解有关它的实现的详细信息才能使用它。这可能不是多态性的地方。
答案 2 :(得分:0)
嗯,也许你可以修改你的第一个继承链:
动物 - 食肉动物 - 狮子 - 老虎 - ...... - 草食动物 - 羊
然后,你可以这样做:
public class Animal<T> where T : Food
{
public abstract T Eats { get; set; }
}
public class Carnivore : Animal<Meat>
{
...
}
我还没有测试过,这只是一个想法,我有......
答案 3 :(得分:-1)
我认为这是一个虚假的困境。食物似乎更接近界面而不是抽象的基类,因为它听起来并不像Meat会与Grass非常相似。相反,请考虑以下内容:
public interface IFood {
public boolean IsForCarnivores();
}
public class Lion : Animal {
...
public override IFood Eats
{
get { ... }
set
{
if (value.IsForCarnivores()) DoSomething(value);
else throw new Exception("I can't eat this!");
}
}
}