Question Pourquoi ne pas hériter de List ?


Lorsque je planifie mes programmes, je commence souvent par une chaîne de pensée comme celle-ci:

Une équipe de football est juste une liste de joueurs de football. Par conséquent, je devrais le représenter avec:

var football_team = new List<FootballPlayer>();

L'ordre de cette liste représente l'ordre dans lequel les joueurs sont listés dans la liste.

Mais je me rends compte plus tard que les équipes ont également d'autres propriétés, en plus de la simple liste des joueurs, qui doivent être enregistrées. Par exemple, le total courant des scores cette saison, le budget actuel, les couleurs uniformes, un string représentant le nom de l'équipe, etc.

Alors je pense:

Ok, une équipe de football est juste comme une liste de joueurs, mais en plus, il a un nom (un string) et un total cumulé de scores (un int). .NET ne fournit pas de classe pour stocker les équipes de football, donc je vais faire ma propre classe. La structure existante la plus similaire et la plus pertinente est List<FootballPlayer>alors j'en hériterai:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

Mais il s'avère que une directive dit que vous ne devriez pas hériter de List<T>. Je suis complètement confus par cette directive à deux égards.

Pourquoi pas?

Apparemment List est en quelque sorte optimisé pour la performance. Comment? Quels problèmes de performance vais-je causer si je prolonge List? Qu'est-ce qui va casser?

Une autre raison que j'ai vu est que List est fourni par Microsoft, et je n'ai aucun contrôle sur elle, donc Je ne peux pas le changer plus tard, après avoir exposé une "API publique". Mais j'ai du mal à comprendre cela. Qu'est-ce qu'une API publique et pourquoi devrais-je m'en préoccuper? Si mon projet actuel n'a pas et ne risque pas d'avoir cette API publique, puis-je ignorer en toute sécurité cette directive? Si j'hérite de List  et il s'avère que j'ai besoin d'une API publique, quelles difficultés vais-je avoir?

Pourquoi est-ce important? Une liste est une liste. Qu'est-ce qui pourrait éventuellement changer? Que pourrais-je vouloir changer?

Et enfin, si Microsoft ne voulait pas que je hérite de List, pourquoi n'ont-ils pas fait la classe sealed?

Quoi d'autre suis-je censé utiliser?

Apparemment, pour les collections personnalisées, Microsoft a fourni un Collection classe qui devrait être étendue au lieu de List. Mais cette classe est très nue, et n'a pas beaucoup de choses utiles, tel que AddRange, par exemple. La réponse de jvitor83 fournit une justification de la performance pour cette méthode particulière, mais comment est un lent AddRange pas mieux que pas AddRange?

Héritant de Collection est beaucoup plus de travail que d'hériter de List, et je ne vois aucun avantage. Sûrement Microsoft ne me dirait pas de faire du travail supplémentaire sans raison, alors je ne peux pas m'empêcher de penser que je suis en train de mal comprendre quelque chose et d'hériter Collection n'est en fait pas la bonne solution pour mon problème.

J'ai vu des suggestions telles que la mise en œuvre IList. Tout simplement pas. C'est des dizaines de lignes de code standard qui ne me rapporte rien.

Enfin, certains suggèrent d'envelopper le List dans quelque chose:

class FootballTeam 
{ 
    public List<FootballPlayer> Players; 
}

Il y a deux problèmes avec ceci:

  1. Cela rend mon code inutilement verbeux. Je dois maintenant appeler my_team.Players.Count au lieu de juste my_team.Count. Heureusement, avec C # je peux définir des indexeurs pour rendre l'indexation transparente, et transmettre toutes les méthodes de l'interne List... Mais c'est beaucoup de code! Qu'est-ce que je reçois pour tout ce travail?

  2. Cela n'a tout simplement aucun sens. Une équipe de football n'a "pas" de liste de joueurs. Il est la liste des joueurs. Vous ne dites pas "John McFootballer a rejoint les joueurs de SomeTeam". Vous dites "John a rejoint SomeTeam". Vous n'ajoutez pas de lettre aux "caractères d'une chaîne", vous ajoutez une lettre à une chaîne. Vous n'ajoutez pas de livre aux livres d'une bibliothèque, vous ajoutez un livre à une bibliothèque.

Je me rends compte que ce qui se passe "sous le capot" peut être considéré comme "ajoutant X à la liste interne de Y", mais cela semble être une façon de penser très contre-intuitive sur le monde.

Ma question (résumé)

Quelle est la manière correcte de représenter une structure de données en C #, qui, "logiquement" (c'est-à-dire "à l'esprit humain") est juste un list de things avec quelques cloches et des sifflets?

Hérite de List<T> toujours inacceptable? Quand est-ce acceptable? Pourquoi pourquoi pas? Qu'est-ce qu'un programmeur doit prendre en compte lorsqu'il décide d'hériter de List<T> ou pas?


975
2018-02-11 03:01


origine


Réponses:


Il y a quelques bonnes réponses ici. Je leur ajouterais les points suivants.

Quelle est la façon correcte de représenter une structure de données en C #, qui, "logiquement" (c'est-à-dire "à l'esprit humain") est juste une liste de choses avec quelques cloches et sifflets?

Demandez à dix personnes non informaticiennes qui connaissent l'existence du football de remplir le champ:

A football team is a particular kind of _____

Fait n'importe qui dites "liste de joueurs de football avec quelques cloches et sifflets", ou ont-ils tous dit "équipe sportive" ou "club" ou "organisation"? Votre idée qu'une équipe de football est un type particulier de liste de joueurs est dans votre esprit humain et votre esprit humain seul.

List<T> est un mécanisme. L'équipe de football est un objet métier - c'est-à-dire, un objet qui représente un concept qui est dans le domaine métier du programme. Ne mélangez pas ceux-là! Une équipe de football Est une sorte de équipe; il a un liste, une liste est une liste de joueurs. Une liste n'est pas un type particulier de liste de joueurs. Une liste est une liste de joueurs. Alors faites une propriété appelée Roster c'est un List<Player>. Et fais le ReadOnlyList<Player> Tant que vous y êtes, à moins que vous ne croyiez que tous ceux qui connaissent une équipe de football puissent supprimer des joueurs de la liste.

Hérite de List<T> toujours inacceptable?

Inacceptable à qui? Moi? Non.

Quand est-ce acceptable?

Lorsque vous construisez un mécanisme qui étend la List<T> mécanisme.

Qu'est-ce qu'un programmeur doit prendre en compte lorsqu'il décide d'hériter de List<T> ou pas?

Est-ce que je construis un mécanisme ou un objet métier?

Mais c'est beaucoup de code! Qu'est-ce que je reçois pour tout ce travail?

Vous avez passé plus de temps à dactylographier votre question qu'il vous aurait fallu écrire des méthodes d'expédition pour les membres concernés de List<T> cinquante fois plus. Vous n'avez clairement pas peur de la verbosité, et nous parlons d'une très petite quantité de code ici; C'est un travail de quelques minutes.

METTRE À JOUR

Je lui ai fait réfléchir et il y a une autre raison de ne pas modeler une équipe de football comme une liste de joueurs. En fait, ce serait une mauvaise idée de modéliser une équipe de football ayant une liste de joueurs aussi. Le problème avec une équipe en tant que / ayant une liste de joueurs est que ce que vous avez est un instantané de l'équipe à un moment donné. Je ne sais pas quelle est votre analyse de rentabilité pour cette classe, mais si j'avais une classe qui représentait une équipe de football, je voudrais poser des questions comme "combien de joueurs de Seahawks ont manqué des matches entre 2003 et 2013?" ou "Quel joueur de Denver qui a déjà joué pour une autre équipe a connu la plus forte augmentation d'une année sur l'autre?" ou "Les Piggers sont-ils allés jusqu'au bout cette année?"

Autrement dit, une équipe de football me semble être bien modélisée une collection de faits historiques Par exemple, lorsqu'un joueur a été recruté, blessé, retiré, etc. Il est évident que la liste actuelle des joueurs est un fait important qui devrait probablement être à l'avant-plan, mais il peut y avoir d'autres choses intéressantes que vous voulez faire avec cet objet. perspective plus historique.


1137
2018-02-11 05:43



Enfin, certains suggèrent d'encapsuler la liste dans quelque chose:

C'est la bonne façon. "Inutile verbeux" est une mauvaise façon de voir cela. Il a une signification explicite lorsque vous écrivez my_team.Players.Count. Vous voulez compter les joueurs.

my_team.Count

..ne signifie rien. Comptez quoi?

Une équipe n'est pas une liste - le composé de plus que juste une liste de joueurs. Une équipe possède des joueurs, donc les joueurs devraient en faire partie (un membre).

Si vous êtes vraiment craignant d'être trop verbeux, vous pouvez toujours exposer les propriétés de l'équipe:

public int PlayerCount {
    get {
        return Players.Count;
    }
}

..which devient:

my_team.PlayerCount

Cela a un sens maintenant et adhère à La loi de Demeter.

Vous devriez également considérer adhérer à Principe de réutilisation composite. En héritant de List<T>, tu dis une équipe est un liste des joueurs et en exposer les méthodes inutiles. C'est faux - comme vous l'avez dit, une équipe est plus qu'une liste de joueurs: elle a un nom, des gestionnaires, des membres du conseil, des entraîneurs, du personnel médical, des plafonds salariaux, etc. disons "Une équipe a un liste des joueurs ", mais il peut aussi avoir d'autres choses.


232
2018-02-11 03:05



Wow, votre message a un tas de questions et de points. La plupart du raisonnement que vous obtenez de Microsoft est exactement sur le point. Commençons par tout à propos de List<T>

  • List<T>  est hautement optimisé. Son utilisation principale est d'être utilisé comme membre privé d'un objet.
  • Microsoft ne l'a pas caché parce que parfois vous pourriez vouloir créer une classe qui a un nom plus convivial: class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... }. Maintenant, c'est aussi simple que de faire var list = new MyList<int, string>();.
  • CA1002: Ne pas exposer les listes génériques: Fondamentalement, même si vous envisagez d'utiliser cette application en tant que développeur unique, il vaut la peine de développer avec de bonnes pratiques de codage, de sorte qu'ils deviennent instillés en vous et une seconde nature. Vous êtes toujours autorisé à exposer la liste en tant que IList<T> si vous avez besoin d'un consommateur pour avoir une liste indexée. Cela vous permet de changer l'implémentation dans une classe plus tard.
  • Microsoft a fait Collection<T> très générique parce que c'est un concept générique ... le nom dit tout; c'est juste une collection. Il y a des versions plus précises telles que SortedCollection<T>, ObservableCollection<T>, ReadOnlyCollection<T>, etc., chacun d'eux IList<T> mais ne pas  List<T>.
  • Collection<T> permet aux membres (c'est-à-dire Add, Remove, etc.) d'être ignorés car ils sont virtuels. List<T> ne fait pas.
  • La dernière partie de votre question est sur place. Une équipe de football est plus que juste une liste de joueurs, donc ce devrait être une classe qui contient cette liste de joueurs. Pense Composition vs héritage. Une équipe de football a une liste de joueurs (une liste), ce n'est pas une liste de joueurs.

Si j'écrivais ce code, la classe ressemblerait probablement à ceci:

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    // Yes. I used LINQ here. This is so I don't have to worry about
    // _roster.Length vs _roster.Count vs anything else.
    public int PlayerCount
    {
        get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

110
2018-02-11 03:26



class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal;
}

Le code précédent signifie: un groupe de gars de la rue jouant au football, et ils ont un nom. Quelque chose comme:

People playing football

Quoi qu'il en soit, ce code (de la réponse de m-y)

public class FootballTeam
{
    // Football team rosters are generally 53 total players.
    private readonly List<T> _roster = new List<T>(53);

    public IList<T> Roster
    {
        get { return _roster; }
    }

    public int PlayerCount
    {
    get { return _roster.Count(); }
    }

    // Any additional members you want to expose/wrap.
}

Moyens: ceci est une équipe de football qui a la gestion, les joueurs, les admins, etc. Quelque chose comme:

Image showing members (and other attributes) of the Manchester United team

Voici comment est présentée votre logique en images ...


104
2018-02-11 15:45



Ceci est un exemple classique de composition contre héritage.

Dans ce cas précis:

Est-ce que l'équipe a une liste de joueurs avec un comportement supplémentaire?

ou

Est-ce que l'équipe est un objet qui contient une liste de joueurs.

En étendant la liste, vous vous limitez de plusieurs façons:

  1. Vous ne pouvez pas restreindre l'accès (par exemple, empêcher les personnes de modifier la liste). Vous obtenez toutes les méthodes de liste si vous avez besoin / voulez tout ou pas.

  2. Que se passe-t-il si vous voulez avoir des listes d'autres choses aussi. Par exemple, les équipes ont des entraîneurs, des gérants, des supporteurs, de l'équipement, etc. Certaines d'entre elles pourraient bien être des listes à part entière.

  3. Vous limitez vos options d'héritage. Par exemple, vous pouvez créer un objet Team générique, puis hériter de BaseballTeam, FootballTeam, etc. Pour hériter de List, vous devez faire l'héritage de Team, mais cela signifie que tous les différents types d'équipe sont forcés d'avoir la même implémentation de cette liste.

Composition - y compris un objet donnant le comportement que vous voulez dans votre objet.

Héritage - votre objet devient une instance de l'objet qui a le comportement que vous voulez.

Les deux ont leurs utilisations, mais c'est un cas clair où la composition est préférable.


81
2018-02-11 10:58



Comme tout le monde l'a souligné, une équipe de joueurs n'est pas une liste de joueurs. Cette erreur est faite par beaucoup de gens partout, peut-être à différents niveaux d'expertise. Souvent, le problème est subtil et parfois très grave, comme dans ce cas. De telles conceptions sont mauvaises car elles violent le Principe de substitution de Liskov. L'Internet a beaucoup de bons articles expliquant ce concept par exemple, http://en.wikipedia.org/wiki/Liskov_substitution_principle 


43
2018-02-11 16:56



Et si le FootballTeam a une équipe de réserve avec l'équipe principale?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
}

Comment modéliserais-tu cela?

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

La relation est clairement a un et pas est un.

ou RetiredPlayers?

class FootballTeam
{
    List<FootballPlayer> Players { get; set; }
    List<FootballPlayer> ReservePlayers { get; set; }
    List<FootballPlayer> RetiredPlayers { get; set; }
}

En règle générale, si vous souhaitez hériter d'une collection, nommez la classe SomethingCollection.

Est-ce que ton SomethingCollection sémantiquement faire sens? Ne faites cela que si votre type est un collection de Something.

Dans le cas de FootballTeam ça ne sonne pas bien. UNE Team est plus qu'un Collection. UNE Team peut avoir des entraîneurs, des entraîneurs, etc comme les autres réponses ont souligné.

FootballCollection ressemble à une collection de ballons de football ou peut-être une collection d'attirail de football. TeamCollection, une collection d'équipes.

FootballPlayerCollection ressemble à une collection de joueurs qui serait un nom valide pour une classe qui hérite de List<FootballPlayer> si tu voulais vraiment faire ça.

Vraiment List<FootballPlayer> est un type parfaitement bon à traiter. Peut être IList<FootballPlayer> si vous le renvoyez d'une méthode.

En résumé

Demande toi

  1. Est  X une Y? ou A  X une Y?

  2. Est-ce que mes noms de classe veulent dire ce qu'ils sont?


34
2018-02-12 11:41



Tout d'abord, cela concerne la convivialité. Si vous utilisez l'héritage, le Team La classe exposera des comportements (méthodes) conçus uniquement pour la manipulation d'objets. Par exemple, AsReadOnly() ou CopyTo(obj) les méthodes n'ont aucun sens pour l'objet d'équipe. À la place du AddRange(items) méthode que vous voudriez probablement un plus descriptif AddPlayers(players) méthode.

Si vous voulez utiliser LINQ, implémenter une interface générique telle que ICollection<T> ou IEnumerable<T> aurait plus de sens.

Comme mentionné, la composition est la bonne façon de s'y prendre. Implémentez simplement une liste de joueurs en tant que variable privée.


27
2018-02-11 03:25



Conception> Implémentation

Quelles méthodes et propriétés vous exposez est une décision de conception. De quelle classe de base vous héritez est un détail d'implémentation. Je pense que cela vaut la peine de revenir en arrière.

Un objet est une collection de données et de comportements.

Donc, vos premières questions devraient être:

  • Quelles données cet objet contient-il dans le modèle que je crée?
  • Quel comportement cet objet présente-t-il dans ce modèle?
  • Comment cela pourrait-il changer dans le futur?

Gardez à l'esprit que l'héritage implique une relation «isa» (est une), alors que la composition implique une relation «has a» (hasa). Choisissez le bon pour votre situation à votre vue, en gardant à l'esprit où les choses pourraient aller au fur et à mesure que votre application évolue.

Pensez à penser dans les interfaces avant de penser à des types concrets, car certaines personnes trouvent plus facile de mettre leur cerveau en «mode conception» de cette façon.

Ce n'est pas quelque chose que tout le monde fait consciemment à ce niveau dans le codage au jour le jour. Mais si vous réfléchissez à ce genre de sujet, vous marchez dans les eaux de conception. En être conscient peut être libérateur.

Considérez les spécificités de conception

Jetez un oeil à la liste <T> et IList <T> sur MSDN ou Visual Studio. Voir quelles méthodes et propriétés ils exposent. Est-ce que ces méthodes ressemblent toutes à ce que quelqu'un voudrait faire à un FootballTeam à votre avis?

Est-ce que footballTeam.Reverse () a du sens pour vous? Est-ce que footballTeam.ConvertAll <TOutput> () ressemble à quelque chose que vous voulez?

Ce n'est pas une question piège; la réponse pourrait vraiment être "oui". Si vous implémentez / héritez List <Player> ou IList <Player>, vous êtes bloqué avec eux; Si c'est l'idéal pour votre modèle, faites-le.

Si vous décidez oui, cela a du sens, et vous voulez que votre objet soit traité comme une collection / liste de joueurs (comportement), et vous voulez donc implémenter ICollection ou IList, par tous les moyens. Notionellement:

class FootballTeam : ... ICollection<Player>
{
    ...
}

Si vous voulez que votre objet contienne une collection / liste de joueurs (données), et que vous voulez donc que la collection ou la liste soit une propriété ou un membre, faites-le. Notionellement:

class FootballTeam ...
{
    public ICollection<Player> Players { get { ... } }
}

Vous pourriez penser que vous voulez que les gens puissent seulement énumérer l'ensemble des joueurs, plutôt que de les compter, de les ajouter ou de les supprimer. IEnumerable <Player> est une option parfaitement valide à considérer.

Vous pourriez penser qu'aucune de ces interfaces n'est utile dans votre modèle. C'est moins probable (IEnumerable <T> est utile dans de nombreuses situations) mais c'est toujours possible.

Toute personne qui tente de vous dire que l'un d'eux est catégoriquement et définitivement faux dans tous les cas est erroné. Toute personne qui tente de vous le dire est catégoriquement et définitivement droite dans tous les cas est erroné.

Passer à la mise en œuvre

Une fois que vous avez décidé des données et du comportement, vous pouvez prendre une décision concernant la mise en œuvre. Cela inclut les classes concrètes dont vous dépendez via l'héritage ou la composition.

Cela peut ne pas être un grand pas, et les gens confondent souvent la conception et la mise en œuvre, car il est tout à fait possible de parcourir tout cela dans votre tête dans une seconde ou deux et commencer à taper.

Une expérience de pensée

Un exemple artificiel: comme d'autres l'ont mentionné, une équipe n'est pas toujours "juste" une collection de joueurs. Conservez-vous une collection de résultats de match pour l'équipe? L'équipe est-elle interchangeable avec le club, dans votre modèle? Si oui, et si votre équipe est une collection de joueurs, c'est peut-être aussi une collection de personnel et / ou une collection de partitions. Ensuite, vous vous retrouvez avec:

class FootballTeam : ... ICollection<Player>, 
                         ICollection<StaffMember>,
                         ICollection<Score>
{
    ....
}

Conception nonobstant, à ce stade de C # vous ne pourrez pas implémenter tout cela en héritant de List <T> de toute façon, puisque C # "only" supporte l'héritage simple. (Si vous avez essayé ce malarky en C ++, vous pouvez considérer cela comme une bonne chose.) L'implémentation d'une collection via l'héritage et une via la composition est susceptible de ressentir sale. Et les propriétés telles que Count deviennent confuses pour les utilisateurs, sauf si vous implémentez explicitement ILIst <Player> .Count et IList <StaffMember> .Count, et qu'elles sont simplement douloureuses plutôt que confuses. Vous pouvez voir où cela se passe. l'intuition tout en pensant à cette avenue pourrait bien vous dire que c'est mal de se diriger dans cette direction (et à tort ou à raison, vos collègues pourraient aussi si vous l'avez mis en œuvre de cette façon!)

La réponse courte (trop tard)

La règle concernant l'héritage des classes de collection n'est pas spécifique à C #, vous la trouverez dans de nombreux langages de programmation. C'est la sagesse reçue, pas une loi. L'une des raisons est que, dans la pratique, la composition est souvent considérée comme gagnante par rapport à l'héritage en termes de compréhensibilité, d'applicabilité et de maintenabilité. Il est plus commun avec les objets du monde réel de trouver des relations "hasa" utiles et cohérentes que des relations "isa" utiles et cohérentes, sauf si vous êtes dans l'abstrait, surtout quand le temps passe et les données et le comportement des objets dans le code changements. Cela ne devrait pas vous empêcher de toujours hériter des classes de collection; mais cela peut être suggestif.


25
2018-02-11 22:27



Une équipe de football n'est pas une liste de joueurs de football. Une équipe de football est composé de une liste de joueurs de football!

C'est logiquement faux:

class FootballTeam : List<FootballPlayer> 
{ 
    public string TeamName; 
    public int RunningTotal 
}

et c'est correct:

class FootballTeam 
{ 
    public List<FootballPlayer> players
    public string TeamName; 
    public int RunningTotal 
}

23
2018-02-11 16:25



ça dépend du contexte

Quand vous considérez votre équipe comme une liste de joueurs, vous projetez l'idée d'une équipe de foot-ball à un aspect: vous réduisez l'équipe aux personnes que vous voyez sur le terrain. Cette projection n'est correcte que dans un certain contexte. Dans un contexte différent, cela pourrait être complètement faux. Imaginez que vous voulez devenir un sponsor de l'équipe. Donc, vous devez parler aux gestionnaires de l'équipe. Dans ce contexte, l'équipe est projetée sur la liste de ses managers. Et ces deux listes ne se chevauchent généralement pas beaucoup. Les autres contextes sont les joueurs actuels par rapport aux anciens joueurs, etc.

Sémantique peu claire

Donc le problème de considérer une équipe comme une liste de ses joueurs est que sa sémantique dépend du contexte et qu'elle ne peut pas être étendue quand le contexte change. De plus, il est difficile d'exprimer le contexte que vous utilisez.

Les classes sont extensibles

Lorsque vous utilisez une classe avec un seul membre (par ex. IList activePlayers), vous pouvez utiliser le nom du membre (et en plus son commentaire) pour rendre le contexte clair. Lorsqu'il y a des contextes supplémentaires, vous ajoutez simplement un membre supplémentaire.

Les cours sont plus complexes

Dans certains cas, il peut être trop difficile de créer une classe supplémentaire. Chaque définition de classe doit être chargée via le chargeur de classe et sera mise en cache par la machine virtuelle. Cela vous coûte des performances d'exécution et de la mémoire. Lorsque vous avez un contexte très spécifique, il peut être bon de considérer une équipe de football comme une liste de joueurs. Mais dans ce cas, vous devriez vraiment utiliser un IList , pas une classe qui en dérive.

Conclusion / Considérations

Lorsque vous avez un contexte très spécifique, vous pouvez considérer une équipe comme une liste de joueurs. Par exemple, dans une méthode, il est tout à fait OK d'écrire

IList<Player> footballTeam = ...

Lors de l'utilisation de F #, il est même possible de créer une abréviation de type

type FootballTeam = IList<Player>

Mais quand le contexte est plus large ou même pas clair, vous ne devriez pas faire cela. C'est particulièrement le cas, lorsque vous créez une nouvelle classe, où il n'est pas clair dans quel contexte il peut être utilisé dans le futur. Un signe d'avertissement est lorsque vous commencez à ajouter des attributs supplémentaires à votre classe (nom de l'équipe, entraîneur, etc.). C'est un signe clair que le contexte dans lequel la classe sera utilisée n'est pas fixe et changera dans le futur. Dans ce cas, vous ne pouvez pas considérer l'équipe comme une liste de joueurs, mais vous devez modéliser la liste des joueurs (actuellement actifs, non blessés, etc.) en tant qu'attributs de l'équipe.


19
2018-02-11 10:10