Oui, la surcharge peut facilement être utilisée de manière excessive.
J'ai découvert que la clé pour déterminer si une surcharge est justifiée ou non est de prendre en compte le public - pas le compilateur, mais le programmeur de maintenance qui arrivera dans quelques semaines/mois/années et qui 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 choses en suspens.
Dans de nombreux cas, si les paramètres passés à GetProducts() sont bien nommés, le responsable de la maintenance sera en mesure de comprendre ce que fait la surcharge - mais cela dépend d'une bonne discipline de nommage au point d'utilisation, que vous ne pouvez pas imposer. Ce que vous pouvez imposer, c'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, c'est-à-dire si elles font la même chose. De cette façon, je ne me soucie pas de la version invoquée par le consommateur de ma classe, puisqu'elles sont équivalentes.
Pour illustrer mon propos, j'utiliserais volontiers des surcharges pour la méthode DeleteFile() :
void DeleteFile(string filePath);
void DeleteFile(FileInfo file);
void DeleteFile(DirectoryInfo directory, string fileName);
Toutefois, pour vos exemples, j'utiliserais des noms distincts :
public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByName(string Name ) {...}
Le fait d'avoir les noms complets rend le code plus explicite pour le responsable de la maintenance (qui pourrait bien être moi). Cela évite les problèmes de 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 est également possible 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 souvent qu'il n'est écrit - même si vous ne revenez jamais au code après l'enregistrement initial dans le contrôle de la source, vous lirez toujours cette ligne de code quelques dizaines de fois pendant que vous écrivez le code qui suit.
Enfin, à moins que vous n'écriviez du code jetable, vous devez permettre à d'autres personnes d'appeler votre code à partir d'autres langages. Il semble que la plupart des systèmes d'entreprise restent en production bien au-delà de leur date limite d'utilisation. Il se peut que le code qui utilise votre classe en 2016 soit écrit en VB.NET, C# 6.0, F# ou quelque chose de complètement nouveau qui n'a pas encore été inventé. Il se peut que le langage ne prenne pas en charge les surcharges.