2009-03-11 8 views
0

J'ai deux chaînes parallèles: l'héritagesécurité Polymorphisme et le type dans les chaînes d'héritage parallèles

Chain1: 
Animal <- Lion 
     <- Gazelle 

Chain2: 
Food <- Meat 
     <- Grass 

Je veux mettre en œuvre la propriété polymorphes « Mange » sur des animaux. Voici à quoi cela ressemble:

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!"); 
    } 
    } 
} 

Cependant, ce code n'est pas de type sécurisé. Devrais-je nourrir mon Lion avec de l'herbe, je ne serai confronté à mon bug que dans l'exécution.

Quelqu'un pourrait-il me fournir un exemple de code qui facilite la sécurité de type en utilisant des génériques sans sacrifier le polymorphisme?

+0

Les lions ne mangent pas d'herbe? Au moins, notre chat avait l'habitude de manger de l'herbe de temps en temps ;-) – Rauhotz

Répondre

1

animal peut être une classe générique:

public abstract class Animal<T> where T : Food 
{ 
    public abstract T Eats {get;set;} 
} 

alors vous pouvez faire un lion animal manger de la viande comme celui-ci

public class Lion : Animal<Meat> 
{ 
    //etc... 
}  

Mais cela est être pas une solution optimale. Vous ne pouvez plus utiliser l'animal comme une interface polymorphe car vous avez besoin de connaître les détails de son implémentation pour l'utiliser. Cela pourrait ne pas être le lieu du polymorphisme.

0

Hmm, peut-être vous pouvez modifier votre première chaîne d'héritage:

animaux - Carnivore - Lion - Tiger - ... - Herbivores - Sheep

Ensuite, vous pourriez peut-être faire quelque chose comme ceci:

public class Animal<T> where T : Food 
{ 
    public abstract T Eats { get; set; } 
} 

public class Carnivore : Animal<Meat> 
{ 
    ... 
} 

Je n'ai pas testé, c'est juste une idée que j'ai ...

-1

Je pense que c'est un peu un faux dilemme. La nourriture semble être plus proche d'une interface qu'une classe de base abstraite, car il ne semble pas que la viande soit très similaire à l'herbe. Au lieu de cela, envisager quelque chose comme:

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!"); 
    } 
    } 
} 
+0

Cela ne rend pas la propriété Eats plus sûre comme elle l'était dans la question originale. Si vous nourrissez Lion avec Grass, vous n'obtiendrez toujours pas d'erreur de compilation. De plus, il ne s'agit pas d'un faux dilemme mais d'un scénario réel. –

2

composition en utilisant de l'héritage:

au lieu d'hériter basé sur le système digestif, brisez la digestion dans son propre ensemble de classes.
D'abord, une interface qui décrit différentes façons de manger.

public interface IDigest 
{ 
    void Eat(Meat food); 
    void Eat(Plant food); 
    void Eat(Offal food); //lol nethack 
} 

Carnivores manger de la viande, peut manger des herbes parfois, et n'aiment pas la merde:

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(); 
    } 
} 

Herbivores sont pointilleux et préfèrent mourir que de manger de la viande (je sais, sauvegardez vos commentaires, c'est un exemple)

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(); 
    } 
} 

Les omnivores mangent n'importe quoi. Témoin une foire d'état.Tous les animaux doivent manger, ils doivent donc avoir un système digestif, ainsi que d'autres systèmes.

public abstract class Animal 
{ 
    /* lots of other stuff */ 
    public IConsume DigestiveSystem {get;set;} 
    /* lots of other stuff */ 
} 

Les hippies sont une classe d'animaux aux goûts connus; il se configure à l'instanciation. Il est également possible d'injecter des comportements et des systèmes de l'extérieur.

public class Hippie : Animal 
{ 
    public Hippie() 
    { 
    /*stuff*/ 
    DigestiveSystem = new Herbivore(); 
    BodyOdorSystem = new Patchouli(); 
    /*more stuff*/ 
    } 
} 

Enfin, voyons un hippie manger un hamburger. Lorsque je modélise des systèmes complexes comme les animaux, je préfère la composition à l'héritage TOUS LES JOURS. Les systèmes complexes peuvent exploser un arbre d'héritage avec la rapidité. Prendre trois systèmes animaux: omnivore/herbivore/carnivore, eau/air/terre, et nocturne/diurne. Ne nous soucions même pas de savoir comment décider quelle classification devient le premier point de différenciation pour les animaux. Allons-nous d'abord Animal au Carnivore, à WaterLiving en premier, ou à Nocturnal en premier?

Puisqu'un omnivore peut vivre dans les airs et préférer la nuit (chauve-souris) et être aussi une créature vivante terrestre (humains), vous devez avoir un chemin d'héritage qui frappe chaque option. C'est un arbre d'héritage avec 54 types différents déjà (c'est tôt, soyez gentil). Et les animaux sont beaucoup plus complexes que cela. Vous pourriez facilement obtenir un arbre d'héritage qui avait des millions de types. Composition sur l'héritage, définitivement.

* Nouvelle-Zélande Chauve-souris à queue courte, par exemple, est omnivore.

+0

En fait, vous n'avez pas répondu à la bonne question, il s'agit du problème de la chaîne d'héritage parallèle. (Le règne animal est juste l'exemple typique de manuel pour le polymorphisme). Si les choses complexes de l'héritage sont en cause, j'irais pour les ontologies. Mais je récompense vos exemples humoristiques! –

+0

La réponse était déjà donnée et acceptée. J'étais plus intéressé à montrer un design alternatif. L'héritage est puissant, mais facilement abusé. – Will

+0

+1 pour "if (Starving) .. else Vomit(); –