Quand dois-je utiliser une classe à 2 propriétés sur une structure pré-construite comme un KeyValuePair?

31

Quand devez-vous placer le type de données Key / Value dans sa propre classe au lieu d'utiliser une structure générique pré-construite, telle que a KeyValuePairou a Tuple?

Par exemple, la plupart des ComboBox que je crée contiennent un DisplayName et une Value. C'est le genre de données que j'essaie de décider quand mettre dans une nouvelle classe et quand utiliser simplement un KeyValuePair.

Je travaille actuellement sur quelque chose qui utilise iCalendar, et les données de l'utilisateur sélectionné sont finalement combinées en un key1=value1;key2=value2;type de chaîne. J'ai commencé par mettre les données dans un KeyValuePair<string,string>, mais maintenant je me demande si cela devrait être sa propre classe à la place.

Dans l'ensemble, je suis intéressé à savoir quelles directives sont utilisées pour décider d'utiliser une structure / classe existante comme un objet KeyValuePairsur un 2 propriétés, et dans quel genre de situations vous utiliseriez les uns par rapport aux autres.

Rachel
la source
3
Quelle langue? En Python, nous n'avons pas ce dilemme, car nous avons le tupletype.
S.Lott
3
@ S.Lott - Le .NET BCL a des tuples depuis la v 4.0.
Oded
1
.NET 4 a également le type de tuple. msdn.microsoft.com/en-us/library/system.tuple.aspx
Dave Nay
1
@Oded Dans ce cas, je construis quelque chose avec iCalendaret je voulais des objets pour BYDAYet BYSETPOS. Ils apparaissent dans les zones de liste déroulante, mais les données réelles sont combinées dans la chaîne de règles récurrentes, qui est un key=value;type de chaîne
Rachel
4
@Rachel: Veuillez mettre à jour la question pour être plus précis. Un tas de commentaires ne sont pas la meilleure façon de clarifier les choses.
S.Lott

Réponses:

26

J'utiliserais généralement un objet plutôt qu'un KeyValuePair ou un tuple dans la plupart des cas. Tout d'abord, lorsque vous arrivez 6 mois plus tard pour apporter des modifications, il est beaucoup plus facile de comprendre quelle était votre intention plus tôt plutôt que de vous demander ce que Tuple tc'est et pourquoi il a ces valeurs amusantes. Deuxièmement, à mesure que les choses grandissent et changent, vous pouvez facilement donner à vos objets de transfert de données un comportement simple selon les besoins. Besoin d'avoir deux formats de nom? Facile, ajoutez simplement les surcharges ToString () appropriées. Vous en avez besoin pour implémenter une interface? Aucun problème. Enfin, il n'y a vraiment pas de surcharge pour créer un objet simple, en particulier avec les propriétés automatiques et l'achèvement du code.

Bonus Protip: si vous voulez empêcher ces objets de polluer votre espace de noms, créer des classes privées à l'intérieur des classes est un excellent moyen de garder les choses secrètes et d'éviter les dépendances étranges.

Wyatt Barnett
la source
1
DTO = objets de transfert de données ? - Je déteste les TLA ;-)
Ben
3
@Ben - TFTFY. . .
Wyatt Barnett
7

La règle pour définir une nouvelle classe est simple: elle est résumée par "Rasoir d'Occam".

http://c2.com/cgi/wiki?OccamsRazor

N'introduisez pas de nouvelles classes sans une bonne raison. Ou utilisez autant que possible des classes prédéfinies. Ou inventez le moins possible. Cependant, vous êtes à l'aise d'écrire moins de code.

Vous ne pouvez pas utiliser une classe prédéfinie si vous devez attribuer une responsabilité unique à l'objet et que cette responsabilité n'est pas définie dans la classe prédéfinie. Il s'agit souvent d'une méthode unique.

Un tupleou KeyValuePairest préféré.

Jusqu'à ce que.

Vous avez besoin de fonctionnalités qui ne font pas partie de A tupleou KeyValuePair. Ensuite, vous devez définir votre propre classe.

S.Lott
la source
3
" Ne jamais concevoir ce que vous pouvez voler ", c'est ma façon préférée de formuler cela.
SingleNegationElimination
1
Considérez-vous la sémantique comme une «fonctionnalité qui ne fait pas partie d'un tupleou KeyValuePair»? KeyValuePair a au moins une forme limitée de sémantique, mais les tuples font rarement (cela pourrait être possible, selon le contexte) à mon humble avis. L'introduction d'une nouvelle classe devrait vous donner une sémantique claire comme une évidence.
Mal Ross
+1 Ne patchez pas un tout dans un bateau avec du ruban adhésif s'il peut s'adapter à une prise.
Evan Plaice
4
Je pense que c'est une utilisation mal placée du rasoir d'Occam. Deuxièmement, il est important de savoir quelles informations sont contenues dans une variable. -1.
Bent
4

Si vous écrivez votre propre classe, vous avez un endroit clair pour mettre la documentation sur les deux valeurs. D'autant plus que deux cordes ne sont pas une définition très claire. Mais vous pourriez écrire quelque chose comme

 public class MyPair : Tuple<string, string>
Signe
la source
J'aime cela parce que MyPairdéfinit les valeurs de Tuple et à quoi elles servent.
IAbstract
2
Mais alors vos propriétés seraient appelées Item1et Item2! N'est-il pas tout aussi facile de définir toute la classe? public class SideStrings { public string Left { get; set; } public string Right { get; set; } }
configurateur
@configurator J'écrivais une réponse à cela et j'ai remarqué que Tuple est en lecture seule, donc je suis d'accord, ce qui, je pense, discute le tout. Bien que vous puissiez envelopper le tuple pour nommer les valeurs comme vous le souhaitez.
Signez le
2
@Sign: Mais à quoi ça sert? Créer votre propre classe demande moins de travail que d'envelopper un tuple.
configurateur
2
@configurator, vous obtenez des éléments d'égalité et de comparaison qui fonctionnent mieux qu'un objet personnalisé, mais si la configuration est requise, le tuple est définitivement la mauvaise voie à suivre.
Signer le
4

S.Lott a écrit

N'introduisez pas de nouvelles classes sans une bonne raison. Ou utilisez autant que possible des classes prédéfinies. Inventez le moins possible.

Vous ne pouvez pas utiliser une classe prédéfinie si vous devez attribuer une responsabilité unique à l'objet qui n'est pas défini dans la classe prédéfinie.

Permettez-moi de dire pourquoi j'ai un sérieux problème avec cela. Puisque

KeyValuePair [chaîne, chaîne]

semble être correct .... KeyValue [chaîne, KeyValue [chaîne, KeyValuePair [chaîne, chaîne]]] est également correct? Je ne sais pas ce que dira à S. Lott, mais mon patron pense que c'est correct.

J'ose être en désaccord. Et voici pourquoi: cela réduit la lisibilité du code qui va suivre. La fonction qui remplira une telle structure de données sera beaucoup plus complexe et sujette aux erreurs (err .. exception) (imaginez au moins une logique métier également). Je n'ai pas trop discuté avec mon patron, mais je vais le dire ici: la lisibilité l'emporte sur les économies d'espace minimes (ce qui était son argument). Un objet de classe ne contient donc pas de champs; mais certains restent vides à certains moments mieux?

PS Je suis un Ultra_Noob alors dites-moi si je me trompe.

Chani
la source
2
Je pense que KeyValuePair<string,string>c'est bien, en supposant que les choses mises en place sont en effet des clés et des valeurs. Ici, la réutilisation d'une classe existante est une victoire, car vous enregistrez l'écriture de code et gagnez en interopérabilité. OTOH, utiliser quelque chose d'aussi compliqué que la KeyValuePair<string,KeyValuePair<string,string>>plupart du temps est tout simplement trop compliqué pour être pratique. Parfois, cela peut être juste - lorsque vous n'avez besoin d'aucune fonctionnalité supplémentaire, lorsque le sens est évident et qu'il fonctionne bien avec d'autres classes. YMMV, c'est juste pondérer les avantages et les contras.
maaartinus
Si vous utilisez C #, voici les performances d'un Tuple vs KeyValuePair . Quelque chose que vous voudrez peut-être partager avec votre patron?
Ben
3

Lorsque nous parlons de paire clé / valeur , je pense généralement aux tables de hachage , ou aux tableaux associatifs , ou simplement à un objet KeyValuePair . Je les utilise tous et il n'y a pas de différence quand j'utilise lesquels.

Je pense que la chose la plus importante à propos d'une liste de paires clé / valeur est que les clés doivent être uniques (car c'est une clé après tout), et toutes les structures susmentionnées me fournissent vraiment cette fonctionnalité.

En dehors de cela, je veux rechercher par clés ou faire des actions de style liste (tableau) comme push et pop.

Donc, ma réponse est NON et je ne crée pas d'objets explicitement pour les paires clé / valeur, car beaucoup de structures intégrées le font déjà pour moi.

Saeed Neamati
la source
3

Le chapitre six de Clean Code a une bonne description du moment où utiliser une structure de données par rapport à une classe. En bref, vous utilisez une structure de données lorsque vous prévoyez principalement d'ajouter de nouvelles fonctions pour opérer sur ces données. Vous utilisez une classe lorsque vous prévoyez principalement d'ajouter de nouveaux types de données à exploiter par les fonctions existantes. Votre ComboBox en est un exemple. Vous disposez d'un ensemble de fonctions (l'implémentation ComboBox) fonctionnant sur de nombreux types de données différents (différents types de données pour différents widgets).

Karl Bielefeldt
la source
2

Je serais peut-être d'accord avec Wilding ici , mais je suis aussi un peu noob à ce genre de chose aussi.

Je dirais que les types intégrés sont souvent des objets de mécanisme . Par exemple, KeyValuePair fait partie du mécanisme d'un dictionnaire et des tables de hachage, ce qui permet de garantir des clés uniques et des propriétés qui ont des noms significatifs dans le contexte du mécanisme lui-même.

D'autre part, l'utilisation d'objets de données personnalisés offre une meilleure représentation de vos données et offre de nombreux avantages, notamment la lisibilité et l'extensibilité. Il n'y a rien pour arrêter de réutiliser le code existant dans des objets construits sur mesure comme le suggère Sign , mais j'essayerais de masquer la classe encapsulée et d'éviter les fuites d'abstraction , afin que vous puissiez modifier l'implémentation sous-jacente à un stade ultérieur sans affecter le reste de votre code .

Donc la vraie question: représentez-vous des données ou utilisez-vous les données dans un mécanisme? (et je ne conseillerais pas de mélanger ces concepts ensemble).

D'après mon expérience, il n'y a rien de pire que de voir un Tuple [string, string] passé dans une méthode et de n'avoir aucun moyen immédiat de savoir ce que Item1 et Item2 sont censés être. Il est préférable d'utiliser Tuples dans les méthodes ou en tant que membres privés d'une classe uniquement.

Ben
la source