39 votes

Meilleures pratiques orientées objet - Héritage v Composition v Interfaces

Je veux poser une question sur la façon dont vous aborderiez un simple problème de conception orientée objet. J'ai quelques idées personnelles sur la meilleure façon d'aborder ce scénario, mais je serais intéressé par les opinions de la communauté Stack Overflow. Les liens vers des articles en ligne pertinents sont également appréciés. J'utilise C#, mais la question n'est pas spécifique au langage.

Supposons que j'écrive une application de vidéothèque dont la base de données comporte un fichier de type Person table, avec PersonId , Name , DateOfBirth et Address champs. Il dispose également d'un Staff qui contient un lien vers une PersonId et un Customer qui renvoie également à PersonId .

Une approche orientée objet simple consisterait à dire qu'une Customer "est un" Person et donc de créer des classes un peu comme ceci :

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer : Person {
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff : Person {
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}

Maintenant, nous pouvons écrire une fonction dire pour envoyer des emails à tous les clients :

static void SendEmailToCustomers(IEnumerable<Person> everyone) { 
    foreach(Person p in everyone)
        if(p is Customer)
            SendEmail(p);
}

Ce système fonctionne bien jusqu'à ce que nous ayons quelqu'un qui soit à la fois un client et un membre du personnel. En supposant que nous ne voulons pas vraiment que notre everyone liste d'avoir la même personne deux fois, une fois en tant que Customer et une fois en tant que Staff faisons-nous un choix arbitraire entre.. :

class StaffCustomer : Customer { ...

et

class StaffCustomer : Staff { ...

De toute évidence, seul le premier de ces deux cas n'enfreindrait pas les règles de l'UE. SendEmailToCustomers fonction.

Alors, que feriez-vous ?

  • Faites le Person ont des références facultatives à une classe StaffDetails et CustomerDetails classe ?
  • Créer une nouvelle classe qui contient un Person plus optionnel StaffDetails et CustomerDetails ?
  • Faire de chaque chose une interface (par exemple IPerson , IStaff , ICustomer ) et créer trois classes qui implémentent les interfaces appropriées ?
  • Adopter une autre approche complètement différente ?

3voto

Paul Croarkin Points 5845

J'éviterais la vérification de "is" ("instanceof" en Java). Une solution consiste à utiliser un Motif décorateur . Vous pourriez créer un EmailablePerson qui décore Person où EmailablePerson utilise la composition pour contenir une instance privée d'une Person et délègue toutes les méthodes non-email à l'objet Person.

1voto

Nous avons étudié ce problème à l'université l'année dernière, nous apprenions eiffel donc nous avons utilisé l'héritage multiple. De toute façon, l'alternative des rôles de Foredecker semble être assez flexible.

1voto

vj01 Points 343

Qu'y a-t-il de mal à envoyer un courriel à un client qui est membre du personnel ? S'il s'agit d'un client, on peut lui envoyer l'e-mail. Ai-je tort de penser ainsi ? Et pourquoi devriez-vous prendre "tout le monde" comme liste d'adresses électroniques ? Ne serait-il pas préférable d'avoir une liste de clients puisque nous avons affaire à la méthode "sendEmailToCustomer" et non à la méthode "sendEmailToEveryone" ? Même si vous voulez utiliser la liste "tout le monde", vous ne pouvez pas autoriser les doublons dans cette liste.

Si aucun de ces éléments n'est réalisable avec beaucoup de redisgn, j'opterai pour la première réponse de Foredecker et peut-être que vous devriez attribuer des rôles à chaque personne.

1voto

fizzer Points 8193

Vos classes ne sont que des structures de données : aucune d'entre elles n'a de comportement, juste des getters et des setters. L'héritage est inapproprié ici.

1voto

Mike D Points 11

Adoptez une autre approche complètement différente : Le problème avec la classe StaffCustomer est que votre membre du personnel pourrait commencer en tant que simple employé et devenir un client plus tard. Vous devriez donc le supprimer en tant qu'employé et créer une nouvelle instance de la classe StaffCustomer. Peut-être qu'un simple booléen dans la classe StaffCustomer de 'isCustomer' permettrait à notre liste de personnes (vraisemblablement compilée à partir de tous les clients et de tous les employés des tables appropriées) de ne pas obtenir le membre du personnel car il saura qu'il a déjà été inclus en tant que client.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X