La Surcharge De Méthode. Pouvez-vous en abusez?

Quelle est la meilleure pratique pour définir plusieurs méthodes qui renvoient la même forme de données avec des filtres différents? Noms de méthodes explicites ou méthodes surchargées?

Par exemple. Si j'ai des produits et que je tire d'une base de données

Manière explicite:

public List<Product> GetProduct(int productId) {    // return a List    }
public List<Product> GetProductByCategory(Category category) {    // return a List    }
public List<Product> GetProductByName(string Name ) {    // return a List    }

Manière surchargée:

public List<Product> GetProducts() {    // return a List of all products    }
public List<Product> GetProducts(Category category) { // return a List by Category }
public List<Product> GetProducts(string searchString ) { // return a List by search string }

Je me rends compte que vous pouvez rencontrer un problème avec signatures similaires , mais si vous passez des objets au lieu de types de base (string, int, char, DateTime, etc), ce sera moins d'un problème. Si... est-ce une bonne idée de surcharger une méthode afin de réduire le nombre de méthodes que vous avez et pour plus de clarté, ou devraient chaque méthode qui filtre les données d'une manière différente ont un autre nom de la méthode?

36
demandé sur Armstrongest 2008-10-29 23:06:34

16 réponses

Oui, la surcharge peut facilement être surutilisée.

J'ai trouvé que la clé pour déterminer si une surcharge est justifiée ou non est de considérer le public-pas le compilateur, mais le programmeur de maintenance qui viendra dans des semaines / mois / années et doit comprendre ce que le code essaie de réaliser.

Un nom de méthode simple comme GetProducts() est clair et compréhensible, mais il laisse beaucoup de non-dits.

Dans de nombreux cas, si le paramètre GetProducts() sont bien nommés, le gars de la maintenance sera capable de déterminer ce que fait la surcharge - mais cela repose sur une bonne discipline de nommage au point d'utilisation, que vous ne pouvez pas appliquer. Ce que vous pouvez appliquer est le nom de la méthode qu'ils appellent.

La ligne directrice que je suis est de ne surcharger les méthodes que si elles sont interchangeables-si elles font la même chose. De cette façon, cela ne me dérange pas quelle version le consommateur de ma classe invoque, car ils sont équivalents.

Pour illustrer, Je serais heureux d'utiliser des surcharges pour une méthode DeleteFile ():

void DeleteFile(string filePath);
void DeleteFile(FileInfo file);
void DeleteFile(DirectoryInfo directory, string fileName);

Cependant, pour vos exemples, j'utiliserais des noms séparés:

public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByName(string Name ) {...}

Avoir les noms complets rend le code plus explicite pour le gars de la maintenance (qui pourrait bien être moi). Il évite les problèmes liés aux collisions de signatures:

// No collisions, even though both methods take int parameters
public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesByDepartment(int departmentId);

Il y a aussi la possibilité d'introduire une surcharge pour chaque objectif:

// Examples for GetEmployees

public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesBySupervisor(Supervisor supervisor);
public IList<Employee> GetEmployeesBySupervisor(Person supervisor);

public IList<Employee> GetEmployeesByDepartment(int departmentId);
public IList<Employee> GetEmployeesByDepartment(Department department);

// Examples for GetProduct

public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductById(params int[] productId) {...}

public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByCategory(IEnumerable<Category> category) {...}
public IList<Product> GetProductByCategory(params Category[] category) {...}

Le Code est lu beaucoup plus qu'il n'est écrit-même si vous ne revenez jamais au code après le vérification initiale dans le contrôle de la source, vous allez toujours lire cette ligne de code quelques dizaines de fois pendant que vous écrivez le code qui suit.

Enfin, sauf si vous écrivez du code jetable, vous devez autoriser d'autres personnes à appeler votre code à partir d'autres langues. Il semble que la plupart des systèmes d'entreprise finissent par rester en production bien au-delà de leur date d'utilisation. Il se peut que le code qui consomme votre classe en 2016 finisse par être écrit VB.NET, C # 6.0, F # ou quelque chose complètement nouveau qui n'a pas encore été inventé. Il se peut que la langue ne supporte pas les surcharges.

44
répondu Bevan 2008-10-30 09:05:57

Pour autant que je sache, vous n'aurez pas moins de méthodes, juste moins de noms. Je préfère généralement le système de nommage de méthode surchargé, mais je ne pense pas que cela fasse vraiment beaucoup de différence tant que vous commentez et documentez bien votre code (ce que vous devriez faire dans les deux cas).

16
répondu Elie 2008-10-29 20:09:29

Pouvez-vous abuser? eh bien, oui, c'est vrai.

Cependant, les exemples que vous avez donnés sont des exemples parfaits d'utilisation de la surcharge de méthode. Ils remplissent tous la même fonction, pourquoi leur donner des noms différents juste parce que vous leur Passez différents types.

La règle principale est de faire la chose la plus claire et la plus facile à comprendre. N'utilisez pas de surcharge juste pour être lisse ou intelligent, faites-le quand cela a du sens. D'autres développeurs peuvent travailler sur ce code. Vous vous voulez le rendre aussi facile que possible pour eux de ramasser et de comprendre le code et être en mesure d'implémenter des modifications sans implémenter de bogues.

15
répondu stephenbayer 2008-10-29 20:13:27

J'aime surcharger mes méthodes pour que plus tard dans intellisense Je n'ai pas un million de la même méthode. Et il me semble plus logique de l'avoir juste surchargé au lieu de l'avoir nommé différemment une douzaine de fois.

11
répondu Jeremy Reagan 2008-10-29 20:10:51

Une chose que vous pouvez considérer est que vous ne pouvez pas exposer les méthodes surchargées en tant que contrats d'opération dans un service Web WCF. Donc, si vous pensez que vous devrez peut-être faire cela, ce serait un argument pour utiliser des noms de méthodes différents.

Un autre argument pour différents noms de méthodes est qu'ils peuvent être plus facilement détectables en utilisant intellisense.

Mais il y a des avantages et des inconvénients pour l'un ou l'autre choix - toute la conception est un compromis.

5
répondu Joe 2008-10-29 20:23:15

Vous avez probablement besoin de normes à l'échelle du projet. Personnellement, je trouve les méthodes surchargées beaucoup plus faciles à lire. Si vous avez le support IDE, allez-y.

4
répondu florin 2008-10-29 20:10:12

Le point de surcharge pour faciliter la courbe d'apprentissage de quelqu'un utilisant votre code... et pour vous permettre d'utiliser des schémas de nommage qui informe l'utilisateur que la méthode ne.

Si vous avez dix méthodes différentes qui renvoient toutes une collection d'employés, générez dix noms différents (surtout s'ils commencent par des lettres différentes!) les fait apparaître comme plusieurs entrées dans la liste déroulante intellisense de vos utilisateurs, prolongeant la longueur de la liste déroulante et cachant le distinction entre l'ensemble des dix méthodes qui renvoient toutes une collection d'employés, et toutes les autres méthodes de votre classe...

Pensez à ce qui est déjà appliqué par. Net framework pour, disons les constructeurs et les indexeurs... Ils sont tous obligés d'avoir le même nom, et vous ne pouvez créer des multiples en les surchargeant...

Si vous les surchargez, ils apparaîtront tous comme un seul, avec leurs signatures disparates et leurs commentaires sur le côté.

Vous ne devriez pas surchargez deux méthodes si elles exécutent des fonctions différentes ou non liées...

Quant à la confusion qui peut exister lorsque vous voulez surcharger deux méthodes avec la même signature par type comme dans

public List<Employee> GetEmployees(int supervisorId);
public List<Employee> GetEmployees(int departmentId); // Not Allowed !!

Eh bien, vous pouvez créer des types distincts en tant que wrappers pour le type de base incriminé pour distinguer les signatures..

  public struct EmployeeId 
  { 
      private int empId;
      public int EmployeeId { get { return empId; } set { empId = value; } }
      public EmployeeId(int employeId) { empId = employeeId; }
  }

  public struct DepartmentId 
  {
   // analogous content
  }

 // Now it's fine, as the parameters are defined as distinct types...
 public List<Employee> GetEmployees(EmployeeId supervisorId);
 public List<Employee> GetEmployees(DepartmentId  departmentId);
4
répondu charles bretana 2008-10-29 21:33:28

J'ai vu une surcharge surutilisée lorsque vous n'avez que des différences subtiles dans les arguments de la méthode. Par exemple:

public List<Product> GetProduct(int productId) { // return a List  }
public List<Product> GetProduct(int productId, int ownerId ) { // return a List  }
public List<Product> GetProduct(int productId, int vendorId, boolean printInvoice) { // return a List  }

Dans mon petit exemple, il devient rapidement difficile de savoir si le deuxième argument int doit être le propriétaire ou l'id client.

1
répondu Alex B 2008-10-29 20:23:04

Un bref coup d'œil sur le cadre devrait vous convaincre que de nombreuses surcharges sont un État de choses accepté. Face à une myriade de surcharges, la conception de surcharges pour la facilité d'utilisation est directement abordée par la section 5.1.1 des directives de conception du Framework Microsoft (Kwalina et Abrams, 2006). Voici un bref prècis de cette section:

  • DO essayez d'utiliser des noms de paramètres descriptifs pour indiquer la valeur par défaut utilisée par shorter surcharge.

  • Évitez de modifier arbitrairement les noms de paramètres dans les surcharges.

  • Évitez que ne soit incohérent dans l'ordre des paramètres dans les membres surchargés.

  • Faites en sorte que ne rende virtuelle que la surcharge la plus longue (si l'extensibilité est requise). Les surcharges plus courtes devraient simplement appeler à une surcharge plus longue.

  • NE PAS utiliser ref ou out paramètres de surcharge membre.

  • Faites autoriser null à être passé pour les arguments optionnels.

  • Utilisez {[11] } la surcharge des membres plutôt que de définir des membres avec des arguments par défaut.

1
répondu Peter Wone 2008-10-29 22:00:50

Oui, vous pouvez l'abuser, mais voici un autre concept qui pourrait aider à garder l'utilisation de celui-ci sous contrôle ...

Si vous utilisez. Net 3.5 + et que vous devez appliquer plusieurs filtres, il est probablement préférable D'utiliser IQueryable et le chaînage, c'est-à-dire

GetQuery<Type>().ApplyCategoryFilter(category).ApplyProductNameFilter(productName);

De cette façon, vous pouvez réutiliser la logique de filtrage partout où vous en avez besoin.

public static IQueryable<T> ApplyXYZFilter(this IQueryable<T> query, string filter)
{
     return query.Where(XYZ => XYZ == filter);
} 
1
répondu JTew 2008-10-30 03:21:47

Une autre option consiste à utiliser un objet Query pour construire la Clause "WHERE". Donc, vous n'auriez qu'une seule méthode comme celle-ci:

public List<Product> GetProducts(Query query)

L'objet de requête contient la condition exprimée de manière orientée objet. Les GetProducts obtiennent la requête en "analysant" l'objet de requête.

Http://martinfowler.com/eaaCatalog/queryObject.html

1
répondu ema 2008-10-30 13:20:49

Vous pouvez utiliser la Surcharge autant que vous le souhaitez. Du point de vue des meilleures pratiques, il est également recommandé d'utiliser la surcharge si vous essayez d'effectuer la même "opération" (de manière holistique) sur les données. Par exemple, getProduct ()

En outre, si vous voyez L'API Java, la surcharge est partout. Vous ne trouverez pas une plus grande approbation que cela.

0
répondu a-sak 2008-10-29 21:49:57

La surcharge est un comportement polymorphe souhaitable. Cela aide le programmeur humain à se souvenir du nom de la méthode. Si explicit est redondant avec le paramètre type, alors c'est mauvais. Si le paramètre type n'implique pas ce que fait la méthode, alors explicit commence à avoir du sens.

Dans votre exemple, getProductByName est le seul cas où explicit pourrait avoir du sens, puisque vous pourriez vouloir obtenir le produit par une autre chaîne. Ce problème a été causé par l'ambiguïté des types primitifs; getProduct (nom n) pourrait être une meilleure solution de surcharge dans certains cas.

0
répondu dongilmore 2008-10-29 22:33:40

Oui, vous pouvez l'abuser. Dans votre exemple, il semblerait que le premier et le troisième retourneraient probablement un seul élément, où le second en retournerait plusieurs. Si c'est correct, alors j'appellerais le premier et le troisième GetProduct et le deuxième GetProducts ou GetProductList

Si ce n'est pas le cas et que les trois renvoient plusieurs (comme si vous le transmettez productID 5, il renvoie tous les éléments avec 5 dans le productid, ou renvoie tous les éléments avec le paramètre string dans son nom) alors je le ferais appelez les trois GetProducts ou GetProductList et remplacez-les tous.

Dans tous les cas, le nom doit refléter ce que fait la fonction, donc l'appeler getproduct (singulier) quand il renvoie une liste de produits ne fait pas un bon nom de fonction. IMNSHO

0
répondu Kevin 2008-10-30 13:33:03

Je suis un fan total de la manière "explicite": donner à chaque fonction un nom différent. J'ai même refactorisé du code qui avait beaucoup de fonctions Add(...) dans le passé, pour AddRecord(const Record&), AddCell(const Cell&), etc.

Je pense que cela aide à éviter certaines confusions, les conversions involontaires (en C++, au moins) et les avertissements du compilateur, et cela améliore la clarté.

Peut-être que dans certains cas, vous avez besoin de l'autre stratégie. Je n'ai pas rencontré un seul instant.

0
répondu Daniel Daranas 2012-01-12 18:04:26

Que diriez-vous de

public IList<Product> GetProducts() { /* Return all. */}

public IList<Product> GetProductBy(int productId) {...}
public IList<Product> GetProductBy(Category category) {...}
public IList<Product> GetProductBy(string Name ) {...}

Et ainsi de suite?

0
répondu AgentFire 2012-05-17 11:47:00