Quand la surcharge de méthode est-elle appropriée?

10

Supposons que je travaille sur un système existant, assez grand. J'ai un objet, myObjectde classe MyClass(par exemple, supposons que je travaille en Java). myObjectest une composition contenant Collection, disons, un Listet d'autres objets qui (je pense) ne sont pas pertinents. Il contient des méthodes déléguées qui servent simplement à faire appel aux méthodes de celui- Listci, afin de s'assurer Listqu'il ne soit pas exposé (désolé si j'ai une mauvaise terminologie).

Disons que Listc'est un List<String>mais, pour une raison quelconque, la méthode d'accès principale est une méthode de masque pour la classe SomeOtherClass. Si je voulais insérer une nouvelle paire de valeurs dans mon List, alors j'aurais un objet SomeOtherClassappelé someObject. J'appellerais myObject.insert(someObject)et à l'intérieur de la insertméthode, il y aurait de la magie qui récupérerait un Stringpour le mettre dans le List<String>.

Supposons maintenant que je n'ai qu'une Stringvaleur et aucun SomeOtherClassobjet à insérer. En supposant que je ne peux pas modifier la insertméthode car cela casserait tout dans ce système. Dois-je alors surcharger la insertméthode? Ou dois-je créer un nouvel objet à SomeOtherClasschaque fois que je veux appeler insert?

Je suppose que si je le surchargeais, ça ressemblerait à quelque chose comme ça ...

public void insert(String s) {
    ...
}

public void insert(SomeOtherObject obj) {
    this.insert(obj.magicStringMethod());
}

(Cet exemple est un puzzle artificiel basé sur une situation similaire (légèrement plus complexe) concernant la surcharge que j'ai rencontrée hier. Je vais l'étendre s'il y a quelque chose qui n'était pas clair)

Serait-ce un endroit approprié pour surcharger une méthode? Sinon, quand devrais-je surcharger une méthode?

blahman
la source
En supposant que Java, avez-vous envisagé d'utiliser des génériques pour résoudre ce problème? Je suppose que ce que je demande vraiment, c'est ce que la chaîne représente vraiment? S'il s'agit réellement d'une représentation d'un objet de domaine réel, il existe un autre moyen potentiel de résoudre ce problème
Martijn Verburg
@MartijnVerburg Je n'ai pas, à ce stade. Comme je ne suis qu'un stagiaire, je ne suis pas encore familier avec des choses comme les modèles de conception et je n'ai pas encore de bonnes habitudes de pratique de conception. En réponse à votre deuxième question, il s'agit d'une représentation d'un objet de domaine réel. magicStringMethod()dans mon exemple, obtiendrait, dans mon cas, une Stringreprésentation de SomeOtherObjectce qui était un objet de domaine.
blahman
Je vous suggère de ne pas surcharger lorsque vous le pouvez. Cela cause parfois de la confusion. Il est préférable d'être certain de la méthode appelée au moment de la conception. Voir ceci: programmers.stackexchange.com/questions/132369/…
NoChance

Réponses:

7

Vous surchargez lorsque vous souhaitez prendre en charge différents types:

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

ou pour prendre en charge une interface progressive utilisant différentes listes de paramètres:

public overload void MyOtherMethod()
{
    this.MyOtherMethod(DefaultValue);
}

public overload void MyOtherMethod(int value)
{
    this.MyOtherMethod(value, DefaultOtherValue);
}

public overload void MyOtherMethod(int value, bool otherValue)
{
    ...
}

Vous pouvez même devenir un peu fou et prendre en charge les deux types différents ET une interface progressive, mais vous devez garder à l'esprit que vous devez éviter de créer des variations de comportement entre les méthodes surchargées. Chaque méthode surchargée doit être fonctionnellement la même que les autres dans un groupe surchargé, sinon il sera difficile de savoir comment, quand ou pourquoi le comportement est modifié. Si vous voulez que deux fonctions fassent quelque chose de complètement différent, vous devez les nommer en conséquence.

S.Robins
la source
7

Je dirais que la surcharge est appropriée lorsque les deux méthodes sont sémantiquement équivalentes. Pour voler des exemples de dukeofgaming:

Ceux-ci sont surchargés de manière appropriée:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a+b;
}

Ce ne sont pas:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a-b;
}

C'est un exemple extrême (si vous ne l'avez pas saisi, la dernière méthode soustrait en fait au lieu d'ajouter), mais l'idée est que si vous avez plusieurs méthodes dans une classe avec le même nom, elles devraient se comporter de manière cohérente.

Dans votre exemple, d'après les informations fournies, ils semblent équivalents (puisque l'un appelle l'autre) et je pense qu'il est raisonnable de les surcharger. Cela ne peut pas faire de mal de demander à une personne plus âgée de votre équipe si vous n'êtes pas sûr de savoir si la méthode doit être ajoutée, mais si vous l'ajoutiez, j'utiliserais le même nom (c'est-à-dire que je la surchargerais).

Gyan aka Gary Buyn
la source
1
Merci d'avoir répondu. J'ai demandé à quelqu'un de plus senior, mais comme le code est dans un état quelque peu intéressant, leur solution était une solution dont je n'étais pas sûr, mais néanmoins mise en œuvre (ce n'était pas la surcharge).
blahman
5

Essentiellement, les paramètres de la méthode dictent le comportement de la méthode.

Un exemple rapide serait:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    //...
}

Si vous passez la méthode sum (a, b) à quelques entiers, elle sait qu'elle doit appeler la première implémentation, car l'appel de méthode coïncide avec la signature de la méthode (c'est-à-dire que la double somme (double, double) ne fonctionnera pas si vous donnez méthode de somme deux entiers).

La signature de l'appel doit correspondre aux implémentations disponibles, donc essayer d'appeler sum (chaîne, chaîne) ne fonctionnera que si vous avez ceci:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    public string sum(String a, String b){
        return "" + (Double.parseDouble(a) + Double.parseDouble(b));
    }
    //...
}

tl; dr: pour que la classe gère la bonne méthode en fonction des paramètres que vous donnez à une méthode du même nom.

Lors de l'héritage, il est appelé remplacement

Un bon exemple de cet autre scénario est lorsque vous souhaitez modifier le comportement par défaut d'une classe en l'héritant, et en particulier lorsque vous avez quelque chose de similaire au modèle de méthode de modèle, où vous avez chaque étape d'un algorithme implémenté dans une méthode.

Imaginez que vous ayez class Robotet une méthode appelée fireAtTarget(Target target)... qui appelle les fireWeaponA(target); fireWeaponB(target); fireWeaponC(target);uns après les autres. Vous voulez également avoir une collection appelée robot_army à laquelle vous ne pouvez ajouter que des objets de la classe Robot. Le robot tire des mitrailleuses par défaut dans ses fireWeaponX()méthodes.

Ensuite, vous voulez avoir class LazorRobotet class MissileRobot, implémentez-vous Robot à nouveau?, Non, il suffit que LazorRobot et MissileRobot héritent de Robot, et surchargent chaque fireWeaponX()méthode pour utiliser Lazorz ou des missiles et vous aurez le même comportement avec différentes armes, sans avoir à réimplémenter le reste des méthodes.

tl; dr: Pour que le comportement de la méthode dépende de la classe sans casser l'interface (robot_army accepte uniquement Robot, mais accepte par extension toutes les classes qui héritent de Robot).

dukeofgaming
la source
Votre premier exemple est un remplacement . Lorsque vous gérez l'interface d'une méthode, changez le comportement d'un descendant. L'implication est que vous n'avez pas l'intention de proposer une méthode alternative. Cependant, votre deuxième exemple est une surcharge correcte. Si votre intention est de mettre en évidence quand remplacer ou quand surcharger, vous souhaiterez peut-être clarifier cela dans votre réponse, sinon toute la when inheritingsection n'est probablement pas nécessaire. :)
S.Robins
Derp, la caféine m'a eu cette fois = P, a laissé la deuxième partie comme première et la première comme deuxième pour l'intérêt général. Merci pour la correction.
dukeofgaming
Si je comprends bien votre réponse, je ne devrais être en surcharge qu'en regardant les paramètres qui me permettront de déduire les différences de comportement entre, disons, sum(String, String)et sum(int, int)?
blahman
@blahman vous devez surcharger lorsque vous souhaitez utiliser différents types, un mélange de types, ou même ajouter des paramètres supplémentaires. La surcharge fournit un moyen de créer des méthodes avec des paramètres par défaut où la syntaxe par défaut param = valeur n'est pas prise en charge. Voir ma réponse pour voir ce que je veux dire.
S.Robins
1
@blahman BTW et sur un autre sujet, lorsque vous avez trop de paramètres et que certains d'entre eux sont facultatifs, il est parfois préférable d'envoyer simplement un seul objet ou une structure de données qui a toutes les valeurs par défaut, de sorte que vous modifiez les attributs de cet objet ou de ces données structure. De cette façon, vous n'avez pas besoin de créer 20 versions de la même méthode simplement pour ajouter un seul paramètre à chaque fois.
dukeofgaming