What\'s better practice when defining several methods that return the same shape of data with different filters? Explicit method names or overloaded methods?
For exa
Yes, overloading can easily be overused.
I've found that the key to working out whether an overload is warranted or not is to consider the audience - not the compiler, but the maintenance programmer who will be coming along in weeks/months/years and has to understand what the code is trying to achieve.
A simple method name like GetProducts() is clear and understandable, but it does leave a lot unsaid.
In many cases, if the parameter passed to GetProducts() are well named, the maintenance guy will be able to work out what the overload does - but that's relying on good naming discipline at the point of use, which you can't enforce. What you can enforce is the name of the method they're calling.
The guideline that I follow is to only overload methods if they are interchangable - if they do the same thing. That way, I don't mind which version the consumer of my class invokes, as they're equivalent.
To illustrate, I'd happily use overloads for a DeleteFile() method:
void DeleteFile(string filePath);
void DeleteFile(FileInfo file);
void DeleteFile(DirectoryInfo directory, string fileName);
However, for your examples, I'd use separate names:
public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByName(string Name ) {...}
Having the full names makes the code more explicit for the maintenance guy (who might well be me). It avoids issues with having signature collisions:
// No collisions, even though both methods take int parameters
public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesByDepartment(int departmentId);
There is also the opportunity to introduce overloading for each purpose:
// 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) {...}
Code is read a lot more than it is written - even if you never come back to the code after the initial check in to source control, you're still going to be reading that line of code a couple of dozen times while you write the code that follows.
Lastly, unless you're writing throwaway code, you need to allow for other people calling your code from other languages. It seems that most business systems end up staying in production well past their use by date. It may be that the code that consumes your class in 2016 ends up being written in VB.NET, C# 6.0, F# or something completely new that's not been invented yet. It may be that the language doesn't support overloads.
You can use Overloading as much as you want. From the best practices point of view as well, it is recommended that you use Overloading if you are trying to perform the same "operation" (holistically) on the data. E.g. getProduct()
Also, if you see Java API, Overloading is everywhere. You wouldn't find a bigger endorsement than that.
I'm a total fan of the "explicit" way: giving each function a different name. I've even refactored some code which had lots of Add(...)
functions in the past, to AddRecord(const Record&)
, AddCell(const Cell&)
, etc.
I think this helps to avoid some confusions, inadvertent casts (in C++, at least) and compiler warnings, and it improves clarity.
Maybe in some cases you need the other strategy. I haven't encountered one yet.
Can you overuse it? well, yes, that is true.
However, the examples you've given are perfect examples of when to use method overloading. They all perform the same function, why give them different names just because you're passing different types to them.
The main rule, is doing the clearest, easiest to understand thing. Don't use overloading just to be slick or clever, do it when it makes sense. Other developers might be working on this code as well. You want to make it as easy as possible for them to pick up and understand the code and be able to implement changes without implementing bugs.
One thing you may consider is that you can't expose overloaded methods as operation contracts in a WCF Web Service. So if you think you may ever need to do this, it would be an argument for using different method names.
Another argument for different method names is that they may be more easily discoverable using intellisense.
But there are pros and cons for either choice - all design is trade-off.
You probably need some project-wide standards. Personally, I find the overloaded methods much easier to read. If you have the IDE support, go for it.