Faut-il renommer des méthodes surchargées?

14

Supposons une interface contenant ces méthodes:

Car find(long id);

List<Car> find(String model);

Est-il préférable de les renommer comme ça?

Car findById(long id);

List findByModel(String model);

En effet, tout développeur qui utilise cette API n'aura pas besoin de regarder l'interface pour connaître les arguments possibles des find()méthodes initiales .

Ma question est donc plus générale: quel est l'avantage d'utiliser des méthodes surchargées dans le code car cela réduit la lisibilité?

Mik378
la source
4
L'une ou l'autre méthode est acceptable tant que vous êtes cohérent.
ChrisF
Il existe une relation entre la surcharge et le remplacement d'une méthode. Cependant cet article favorise votre suggestion - Il peut être intéressant: roseindia.net/javatutorials/…
NoChance

Réponses:

23

Il s'agit d'un problème relativement mineur par rapport à de nombreuses autres mauvaises pratiques de lisibilité auxquelles vous pourriez être sensible, donc je dirais que c'est principalement une question de goût comment vous nommez vos méthodes.

Cela dit, si vous voulez faire quelque chose, je suivrais cette pratique:

  • Surcharge si ...

    Les méthodes obéissent à peu près au même contrat mais fonctionnent simplement sur des entrées différentes (imaginez un opérateur téléphonique qui peut rechercher votre compte par votre numéro d'identification fiscale, votre numéro de compte ou votre nom et votre anniversaire). Cela inclut le retour du même type de sortie .

  • Utilisez un nom différent si ...

    Les méthodes font des choses sensiblement différentes ou retournent des sorties différentes (comme votre cas). Vous pourriez envisager d'utiliser un nom différent si l'un accède à la base de données et l'autre pas.

    De plus, si le type retourné est différent, je changerais également le verbe pour indiquer que:

    Car findById(long id);
    
    List findAllByModel(String model);
    
Nicole
la source
3
FWIW, je dirais cela un peu plus fortement: "Les méthodes obéissent exactement au même contrat ..." C'est-à-dire que le type / nombre d'arguments n'a pas d'importance - la sémantique de l'appel de fonction est identique malgré tout. Si le type / nombre d'arguments est important, vous ne devez pas surcharger.
mcmcc
4

Je recommanderais d'utiliser un nom différent, dans tous les cas. Il est possible qu'à un moment donné dans le futur, vous souhaitiez ajouter une autre méthode, disons List<Car> findByMake(String make), contrairement à List<Car> findByModel(String model). Alors tout à coup, tout appeler findcesse de faire sens. Vos méthodes sont également moins susceptibles d'être mal utilisées par inadvertance, si leurs noms donnent plus d'informations sur la façon dont elles doivent être utilisées.

Dawood dit réintégrer Monica
la source
1
Pour être honnête, ce ne serait pas autant un problème si la fonctionnalité était représentée plus explicitement par les objets: find(Make val)et find(Model val). Ensuite, des méthodes pratiques telles que celles- findByMake(String val)ci seraient beaucoup plus claires sur ce qu'elles font réellement. Après tout, a Stringn'est pas une marque ou un modèle, donc la méthode doit expliquer ce qu'elle fait vraiment.
Nicole
4

Si vous renommez une méthode, elle ne sera plus surchargée. En soi, la surcharge ne rend pas nécessairement le code moins lisible, mais elle peut rendre l'implémentation plus difficile à suivre si la syntaxe n'est pas claire.

De nombreux langages utilisent la surcharge de méthode comme moyen de présenter une interface vers des fonctionnalités où les paramètres peuvent être facultatifs et les valeurs par défaut des paramètres facultatifs sont implicites. Cela est particulièrement vrai pour les langues qui ne prennent pas en charge une syntaxe de paramètre par défaut dans la déclaration de méthode.

Ce faisant:

void MyMethod(int param1, int param2 = 10)
{
    ...
}

vous évite de le faire:

void MyMethod(int param1)
{
    MyMethod(param1, Param2Default);
}

void MyMethod(int param1, int param2)
{
    ....
}

Quant à ce qui est plus lisible, cela dépend vraiment de vous. Personnellement, je préfère la deuxième option, en particulier lorsque la liste des paramètres devient un peu longue, mais je suppose que cela n'a pas vraiment d'importance tant que vous êtes cohérent tout au long de votre API.

La difficulté de surcharge survient lorsque vous voulez des fonctions qui font essentiellement la même chose et où vous voulez que les listes de paramètres soient les mêmes, mais que les types de retour soient différents. La plupart des langues ne savent pas différencier deux méthodes nommées de la même manière, mais avec des types de retour différents. À ce stade, vous devez penser à utiliser des génériques, à modifier l'interface des paramètres ou à renommer l'une de vos méthodes pour indiquer la différence de type de retour. C'est là que la lisibilité peut devenir un gros problème, si vous ne vous contentez pas d'un schéma de nommage simple et clair pour faire face à des situations comme celle-ci.

Nommer vos méthodes surchargées GetSomething()et GetSomethingEx()ne va pas en dire long sur les différences entre vos méthodes, en particulier si ce sont les types de retour qui sont les seules différences entre elles. D'un autre côté, GetSomethingAsInt()et GetSomethingAsString()vous en dire un peu plus sur ce que font les méthodes, et bien qu'il ne s'agisse pas strictement d'une surcharge, indiquez que les deux méthodes font des choses similaires, mais retournent des types de valeurs différents. Je sais qu'il existe d'autres façons de nommer les méthodes, mais pour illustrer ce point, ces exemples grossiers devraient le faire.

Dans l'exemple OPs, le renommage n'est pas strictement nécessaire car les paramètres de méthode sont différents, mais cela rend les choses un peu plus claires pour nommer une méthode plus spécifiquement. En fin de compte, cela se résume vraiment au type d'interface que vous souhaitez présenter à vos utilisateurs. La décision de ne pas surcharger ne doit pas être prise uniquement en fonction de votre propre perception de la lisibilité. La surcharge des méthodes peut par exemple simplifier une interface API et réduire le nombre de méthodes dont un développeur peut avoir besoin de se souvenir, d'autre part, elle peut obscurcir l'interface à un degré qui oblige ensuite un développeur à lire la documentation de la méthode pour comprendre quelle forme de méthode à utiliser, alors que le fait d'avoir un certain nombre de méthodes nommées de manière similaire mais descriptive peut rendre plus évident la simple lecture d'un nom de méthode quant à son objectif.

S.Robins
la source
0

Privilégiez la surcharge tant que les méthodes retournent la même chose et suivent le même contrat. La surcharge libère le code appelant de la validation inutile du type de paramètre.

Supposons que la fonction appelante reçoive une requête de recherche en tant que paramètre et effectue un autre traitement avant et / ou après l'appel à find.

void tryToSellCars(String which) {
    /* grab an airhorn, inflatable tube guy... */
    List<Car> cars = find(which);
    /* expound virtues of each car in detail... */
}

Si vous souhaitez modifier le type de cette requête pour une raison quelconque (par exemple, d'une simple chaîne d'ID à un objet de requête complet), vous pouvez effectuer ce changement dans la fonction d'appel simplement en changeant la signature de la fonction pour accepter le nouveau type de paramètre sans vous soucier de changer la méthode qu'il appelle sur votre classe.

void tryToSellCar(CarQuery which) {
    /* grab airhorn, inflate tube guy... */
    List<Car> cars = find(which)
    /* expound virtues of each car in detail... */
}

Si vous implémentez findByIdet findByQueryObjectséparément, vous devrez rechercher chaque appel pour effectuer ce changement. Dans l'exemple, je n'ai changé qu'un seul mot et j'avais terminé.

sqykly
la source
Cette réponse suppose, bien sûr, que vous utilisez un langage qui prend en charge la surcharge avec des erreurs de compilation pour un paramètre non valide. Si vous écrivez JavaScript ou Ruby ou tout autre langage qui ne prend pas en charge la surcharge en mode natif, j'utiliserais toujours le verbose findByFoopour détecter les incompatibilités de type plus tôt.
sqykly