Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 .NET Discussion :

Héritage ou interface


Sujet :

.NET

  1. #1
    Nouveau membre du Club
    Héritage ou interface
    Bonjour,

    Déjà je vous assure que j'ai parcouru pas mal de forum et cours avant de venir poser la question ici. Je ne trouve pas la réponse à ce cas qui pourtant à mon avis est simple...
    je veux faire une classe (ou interface) pour pourvoir déclarer de manière général un objet et ensuite en fonction du choix de l'utilisateur (que j'ai simulé ici avec un random) je veux pouvoir utiliser des méthodes qui ont le même nom pour chacun d'entre eux mais qui sont néanmoins différentes de par leurs signatures. Voila ce que j'ai écrit pour l'exemple mais qui ne compile pas :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    public class Animal {  }
     
     public class Poisson : Animal
        {
            public void Manger(string nourriture)
            {
                Console.WriteLine("Le poisson mange" + nourriture);
            }
        }
     
     public class Chien : Animal
        {
            public void Manger(string nourriture,string boisson)
            {
                Console.WriteLine("le chien mange" + nourriture +" et "+ boisson);
            }
        }
     
    class Program
        {
            static void Main(string[] args)
            {
                Animal animal = new Animal();
                var _random = new Random(); //Simule le choix de l'utilisateur
                decimal tirage = _random.Next(2);
                if (tirage < 1)
                {
                    Chien medor = (Chien)animal;
                }
                else
                {
                    Poisson bubulle = (Poisson)animal;
                }
                Console.WriteLine(tirage + " donc " + animal.GetType());
     
                if (animal.GetType() == typeof(Chien))
                {
                    animal.Manger("croquettes", "eau");
                 }
                else
                {
                    animal.Manger("nourriture pour poissons");
                 }
     
                Console.ReadLine();
            }
        }


    j'ai tenté de caster animal, j'ai tenté de définir 2 méthodes dans Animal avec des signatures différentes pour renvoyer vers la bonne classe... je n'y arrive pas. L'idée est de définir un Animal de manière générale dans mon code et en fonction du choix de l'utilisateur d'adapter la méthode appelée...

  2. #2
    Nouveau membre du Club
    J'ai (enfin) réussi, je vous mets ce que j'ai trouvé, n'hésitez pas à me corriger. Merci d'avance

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    class Program
        {
            static void Main(string[] args)
            {
                Animal animal = new Animal();
                Chien medor=new Chien();
                Poisson bubulle= new Poisson();
                var _random = new Random();
                decimal tirage = _random.Next(2);
                string message;
                if (tirage < 1)
                {
                    animal = new Chien();
                    message = "chien";
                }
                else
                {
                    animal =new Poisson();
                    message = "poisson";
                }
     
                Console.WriteLine(message);
     
                if (animal.GetType()==typeof(EssaiPerso.Chien))
                {
                    medor.Manger("des croquettes", "de l'eau");
                }
                else
                {
                    bubulle.Manger("nourriture pour poissons");
                }
     
                Console.ReadLine();
            }
        }

  3. #3
    Expert éminent sénior
    une classe à hériter c'est pratique quand il y a du code partagé dans celle ci
    une interface sert à partager des fonctionnalités, c'est ce qui conviendrait mieux ici pour toi

    interface IManger avec la méthode manger(string nourriture) et interface IBoire avec la méthode Boire(string boisson)

    il faut implémenter les interfaces nécessaires à chaque classe (donc pas IBoire sur poisson mais les 2 interfaces sur chien)

    ensuite si dans une collection tu as des chiens et des poissons tu peux faire
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    if (animal is IManger m) m.Manger(nourriture) // syntaxe qui test le type et créé une variable typée en même temps
    if (animal is IBoire b) b.Boire(boisson)

    on teste donc si un élément a la fonctionnalité auquel cas on peut l'utiliser

    après tu peux quand même avoir une classe animal en base pour les 2 s'il y a un intérêt (comme des propriétés ou méthodes communes, ou un besoin de sérialisation avec héritage)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  4. #4
    Expert éminent sénior
    ta méthode avec gettype pourrait fonctionner (et encore il en manque un bout*) mais ce n'est pas idéal
    si tu te retrouves avec 40 types d'animaux tu avoir pas mal de if à faire, alors qu'avec les interfaces un seul if suffit pour une fonctionnalité

    * ici tu as déclaré medor en tant que chien donc tu as accès à la méthode qui demande 2 string
    si à un moment tu as une variable de type animal, visual studio ne te laissera pas écrire ce code, vu que cette méthode n'existe que sur chien
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Nouveau membre du Club
    Merci de tes commentaires.
    oui je pense avoir besoin en effet de ce que tu appelles sérialisation, je voudrais pouvoir gérer l'animal de manière général quand c'est possible avant de le spécialiser quand cela est nécessaire.
    Je comprends ton principe d'interfaces IManger et IBoire mais je ne pense pas pouvoir l'utiliser dans ce cas.
    Il semble donc que l'héritage soit le mieux.
    le "is" est il aussi approprié sans l'utilisation d'interfaces?

    EDIT : oui le "is" marche trés bien et est moins verbeux.
    Je pense conserver cette façon de faire car je n'aurais que peu de propositions dans mon application réelle.
    Peut on imaginer utiliser un switch case dans ce cas?

  6. #6
    Expert éminent sénior
    sur c#8 le switch permet plein de choses

    avant ca le switch permet de faire du typage, à priori avec la syntaxe suivante
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    switch (unanimal)
    {
        case Chien lechien:
            lechien.methode();
            break;
        case Poisson lepoisson:
            lepoisson.autremethode();
            break;
        default:
            throw new applicationexception();
            break;
    }



    sinon il y a as
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    var lechien = unanimal as chien;
    if (lechien != null) ...

    si c'est un chien ca le cast et le range dans la variable, sinon ca renvoie null;
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  7. #7
    Nouveau membre du Club
    Merci encore pour toutes ces différentes solutions

  8. #8
    Membre éprouvé
    Il manque 2 mots clefs dans ton code initial, si tu veux utiliser l'héritage, plutôt que les interfaces: virtual et override

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
        class Animal
        {
            public virtual string Manger()
            {
                return "inconnu";
            }
        }
    
        class Chien : Animal
        {
            public override string Manger()
            {
                return "canigou";
            }
        }
    
        class Chat : Animal
        {
            public override string Manger()
            {
                return "kitekat";
            }
        }


    Ainsi, je pourrais produire ce code appelant :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
                var UnChien = new Chien();
                var UnChat = new Chat();
     
                var Animaux = new List<Animal>();
                Animaux.Add(UnChien);
                Animaux.Add(UnChat);
     
                foreach( var a in Animaux)
                    MessageBox.Show(a.Manger());


    Animaux est une liste de Animal, mais pas besoin de caster ou de faire des gettype pour que ce soit la méthode de l'enfant qui soit appelé.
    Le programme produira 2 messages, l'un retournant canigou, l'autre kitekat

    Si Animal n'est jamais implémenté, mais toujours dérivé, on peux alors rendre la classe (et la méthode) : abstract
    Dans ce cas, on ne donne plus de corps à la méthode Animal.Manger. Elle sera implémenter dans chaque enfants.

    L'avantage d'une class abstract et de l'héritage sur une interface, c'est que je peux, dans l'ancêtre implémenter des méthodes et propriétés communes dans l'ancêtre.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        
        abstract class Animal
        {
            public int NombrePattes { get; set; }
    
            public abstract string Manger();
        }


    Chien et Chat (et Rouge-Gorge, histoire que ce ne soit pas tout le temps 4) posséderont la propriétés NombrePattes sans avoir à l'implementer dans chacune, mais devront toutes 2 implémenter Manger, pour que cela compile.


    L'avantage des interfaces, cela permet de définir (certifier - on parle même de contrat) qu'une classe présente des fonctions particulières - fonctions qui peuvent être communes à plusieurs classes qui n'ont rien à voir entre elle.
    Du coup, ces classes n'ont pas besoin d'ancêtre commun.
    Une classe peux présenter plusieurs interfaces différentes et n'ayant rien à voir entre elles non plus.

    Dans le Framework .Net, par exemple, il y a l'interface IDisposable.
    Une classe disposable présente une méthode Dispose(), qui permet d'explicitement libérer des ressources d'un l'objet, sans avoir à attendre que celui-ci soit détruit par le garbage collector.

    Il y a des objets disposable dans tous les domaines et dans des classes qui n'ont rien à voir les une avec les autres.

    Tu pourrais avoir une seconde classe ancêtre "Plante" que tu dérives pour avoir des "poireaux" et des "pétunia".
    Ces classes n'ont rien à voir avec Animal. Elle n'ont pas besoin de NombrePattes, mais vont avoir de toutes autres propriétés et méthodes.

    Mais si maintenant, tu veux nourrir tout ton Zoo : plantes et animaux.
    Tu pourrais dire que Animal et Plante présente tous les deux l'interface INourrir.
    Et tu pourrais faire un truc du style :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    var EtresVivants = new List<INourrir>();
     
    // ajouter tant des Animaux que des plantes à EtresVivant, puis ...
     
    foreach( var e in EtresVivants ) 
       e.Nourrir();


    Ce n'est donc pas Héritage OU Interface.
    Ce sont 2 mécanismes différents qui ont des finalités différentes et qui ne s'exclus pas l'un l'autre.
    --
    vanquish

  9. #9
    Expert éminent sénior
    Citation Envoyé par vanquish Voir le message
    Il manque 2 mots clefs dans ton code initial : virtual et override
    ses 2 méthodes ont une signature différente, ce n'est donc pas un problème de déclaration mais de conception
    et là seul vérifier le type lui convient ...
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  10. #10
    Membre éprouvé
    Citation Envoyé par Pol63 Voir le message
    ses 2 méthodes ont une signature différente, ce n'est donc pas un problème de déclaration mais de conception
    et là seul vérifier le type lui convient ...
    Effectivement, j'ai lu trop vite.
    Je me suis braqué sur les switch à 40 possibilités qui comme tu l'indique est une ineptie du point de vue de la conception.
    --
    vanquish

  11. #11
    Nouveau membre du Club
    Pol63 a répondu avant que je ne repasse par ici :
    en effet mes 2 méthodes ont des signatures différentes et ne sont donc pas identiques, de plus je ne veux pas rendre ma classe mère abstraite car je suis justement intéressé par le fait de pouvoir déclarer des objets de manière "générale" et pouvoir ensuite tester leur type pour adapter leur comportement.
    Je comprends votre réticence pour mon switch case ou if( ... is ....) mais je n'aurais pas a déclarer 50 types différents dans mon application, il est même fort probable que l'on s'arrête à 2.

    Je vous remercie encore une fois pour ces rappels