9 votes

Surcharges et arguments génériques

J'ai une question à poser. Dans le framework, qui a été écrit en grande partie avant l'arrivée des génériques, on voit souvent une fonction avec beaucoup de surcharges pour faire quelque chose avec différents types.

a)

Parse(int data)
Parse(long data)
Parse(string data)
..etc

Il semble que cela ne pose pas de problème, car cela permet de réduire le code pour chaque méthode, etc. D'un autre côté, avec les génériques, vous pouvez faire ce qui suit :

b)

Parse<T>(T data)

puis des instructions de type ifs/switch avec typeof() pour essayer de déduire quels sont les types et ce qu'il faut en faire.

Quelles sont les meilleures pratiques ? Ou quelles sont les idées qui m'aideraient à choisir entre a) et b) ?

25voto

Stefan Steinegger Points 37073

Selon moi, si vous avez besoin d'instructions de type "if/switch", il est préférable de les surcharger. Les génériques doivent être utilisés lorsque l'implémentation ne dépend pas du type concret pour pouvoir être réutilisée.

Il s'agit donc d'une règle générale :

  • surcharge s'il y a une implémentation séparée pour chaque type
  • Utilisez les génériques si vous pouvez avoir une implémentation unique qui fonctionne pour tous les types possibles.

8voto

Robert Cartaino Points 12173

Code Smell.

Si vous avez " une sorte d'interrupteur "Il s'agit d'une odeur de code qui se contente de crie au polymorphisme . Elle suggère que les génériques sont pas la solution à ce problème. Les génériques doivent être utilisés lorsque le code ne dépend pas des types concrets vous y passez.

Regardez cette vidéo de Google Tech Talks : " Les Clean Code Talks -- Héritage, polymorphisme et tests ". Il traite spécifiquement de ce dont vous parlez.

4voto

Joseph Points 18099

Le modèle que vous décrivez, où l'utilisation des génériques se traduit par un tas de déclarations ifs/switch, est un anti-modèle.

Une solution consiste à mettre en œuvre un modèle de stratégie, qui vous permet d'utiliser des éléments génériques, tout en isolant les préoccupations de la méthode Parse, qui ne doit pas savoir comment traiter chaque cas différent.

Ejemplo:

class SomeParser
{
    void Parse<T>(ParseStrategy<T> parseStrategy, T data)
    {
        //do some prep

        parseStrategy.Parse(data);

        //do something with the result;
    }
}

class ParseStrategy<T>
{
    abstract void Parse(T data);
}

class StringParser: ParseStrategy<String>
{
    override void Parse(String data)
    {
        //do your String parsing.
    }
}

class IntParser: ParseStrategy<int>
{
    override void Parse(int data)
    {
        //do your int parsing.
    }
}

//use it like so
[Test]
public void ParseTest()
{
    var someParser = new SomeParser();
    someParser.Parse(new StringParser(), "WHAT WILL THIS PARSE TO");
}

et vous serez alors en mesure de passer dans n'importe quelle stratégie que vous développerez. Cela vous permettrait d'isoler correctement vos préoccupations dans plusieurs classes et de ne pas enfreindre le principe de responsabilité unique (SRP).

2voto

Reed Copsey Points 315315

Si vous avez besoin d'instructions if/switch pour faire fonctionner les génériques, vous avez probablement un problème plus important. Dans cette situation, il est très probable que l'argument générique ne fonctionne pas (correctement) pour CHAQUE type, mais seulement pour un ensemble fixe de types que vous manipulez. Dans ce cas, il est préférable de fournir des surcharges pour gérer les types spécifiques individuellement.

Cela présente de nombreux avantages :

  • Il n'y a aucun risque d'abus - l'utilisateur ne peut pas transmettre un argument non valide.
  • Votre API est plus claire - il est très évident que les types sont appropriés.
  • Les méthodes génériques sont plus compliquées à utiliser et ne sont pas aussi évidentes pour les utilisateurs débutants.
  • L'utilisation d'un générique suggère que n'importe quel type est valide - ils devraient vraiment fonctionner avec tous les types.
  • La méthode générique sera probablement plus lente, du point de vue des performances.

Si votre argument PEUT fonctionner avec n'importe quel type, cela devient moins clair. Dans ce cas, j'envisagerais souvent d'inclure les surcharges, ainsi qu'une méthode générique de repli pour les types. Cela permet d'améliorer les performances lorsque vous passez un type "attendu" à la méthode, mais vous pouvez toujours travailler avec d'autres types non attendus.

1voto

Adam Robinson Points 88472

Bien qu'il n'y ait pas de règle unique en la matière, les génériques doivent être utilisés lorsque l'élément le type spécifique n'est pas pertinent . Cela ne veut pas dire que vous ne pouvez pas contraindre le type, mais ce type spécifique n'a pas vraiment d'importance. Dans ce cas, la méthode d'analyse est entièrement dépendante (et différente) de chaque type. Les génériques ne semblent pas être une bonne solution dans ce cas.

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