在定义多个使用不同过滤器返回相同形状数据的方法时,有什么更好的做法?显式方法名称或重载方法?
例如。如果我有一些产品而且我从数据库中提取
明确的方式:
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 }
重载方式:
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 }
我意识到你可能会遇到类似签名的问题,但是如果你传递的是对象而不是基类型(string,int,char,DateTime等),那么这将是一个问题。问题。所以...... 重载一个方法来减少你拥有的方法数量是个好主意,为了清晰起见,或应该每个方法以不同的方式过滤数据具有不同的方法名称?
答案 0 :(得分:44)
是的,重载很容易被滥用。
我发现确定过载是否合理的关键是考虑受众 - 而不是编译器,而是维护程序员将在数周/数月/年内出现并且必须了解什么代码正试图实现。
像GetProducts()这样的简单方法名称是清晰易懂的,但它确实留下了很多未说明的内容。
在许多情况下,如果传递给GetProducts()的参数命名良好,维护人员将能够计算出重载的作用 - 但这依赖于在使用点的良好命名规则,你可以'强制执行。您可以强制执行的是他们正在调用的方法的名称。
我遵循的指导原则是只有在可以互换的情况下才重载方法 - 如果他们做同样的事情。这样,我不介意我班级的消费者调用哪个版本,因为它们是等价的。
为了说明,我很乐意为DeleteFile()方法使用重载:
void DeleteFile(string filePath);
void DeleteFile(FileInfo file);
void DeleteFile(DirectoryInfo directory, string fileName);
但是,对于您的示例,我将使用不同的名称:
public IList<Product> GetProductById(int productId) {...}
public IList<Product> GetProductByCategory(Category category) {...}
public IList<Product> GetProductByName(string Name ) {...}
拥有全名使得维护人员(可能是我)的代码更加明确。它避免了签名冲突的问题:
// No collisions, even though both methods take int parameters
public IList<Employee> GetEmployeesBySupervisor(int supervisorId);
public IList<Employee> GetEmployeesByDepartment(int departmentId);
还有机会为每个目的引入重载:
// 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) {...}
代码读取的次数比写入的多得多 - 即使您在初始检查源代码控制后再也没有回到代码中,您仍然会在几十次读取该行代码时你编写了下面的代码。
最后,除非您正在编写一次性代码,否则您需要允许其他人使用其他语言调用您的代码。似乎大多数业务系统最终都会在日期之前保持生产状态。可能是2016年消耗你的类的代码最终是用VB.NET,C#6.0,F#或者尚未发明的全新内容编写的。可能是语言不支持重载。
答案 1 :(得分:16)
据我所知,你不会有更少的方法,只有更少的名字。我通常更喜欢重载的命名方法系统,但我认为只要你很好地评论和记录你的代码(在任何一种情况下都应该这样做),它并没有太大的区别。
答案 2 :(得分:15)
但是,您提供的示例是何时使用方法重载的完美示例。它们都执行相同的功能,为什么只是因为你将不同的类型传递给它们而给它们不同的名称。
主要规则是做最清楚,最容易理解的事情。不要使用重载只是为了光滑或聪明,在有意义的时候去做。其他开发人员也可能正在处理此代码。您希望让它们尽可能简单地获取并理解代码,并能够在不实现错误的情况下实现更改。
答案 3 :(得分:11)
我喜欢重载我的方法,以便以后在intellisense中我没有相同的方法。对我来说,让它过度负载似乎更合乎逻辑,而不是让它以不同的方式命名十几次。
答案 4 :(得分:5)
您可以考虑的一件事是您不能将重载的方法公开为WCF Web服务中的操作契约。因此,如果您认为您可能需要这样做,那么使用不同的方法名称将是一个争论。
不同方法名称的另一个论点是使用intellisense可以更容易地发现它们。
但两种选择都有利弊 - 所有的设计都需要权衡。
答案 5 :(得分:4)
您可能需要一些项目范围的标准。就个人而言,我发现重载方法更容易阅读。如果你有IDE支持,那就去吧。
答案 6 :(得分:4)
重载的目的是为了简化某人使用您的代码的学习曲线......并允许您使用命名方案来告知用户该方法的用途。
如果您有十种不同的方法都返回一组员工,那么生成十个不同的名称(特别是如果它们以不同的字母开头!)会导致它们在用户的intellisense下拉列表中显示为多个条目,从而扩展下拉的长度,以及隐藏所有返回员工集合的十个方法的集合之间的区别,以及您班级中的其他任何方法......
考虑一下.Net框架已经强制执行的内容,比如构造函数和索引器......它们都被强制使用相同的名称,并且只能通过重载它们来创建多个...
如果你超载它们,它们将全部显示为一个,它们的不同签名和注释都在一边。
如果两个方法执行不同或不相关的函数,则不应重载这两个方法......
当您希望按类型重载具有相同签名的两个方法时可能存在的混淆 如在
public List<Employee> GetEmployees(int supervisorId);
public List<Employee> GetEmployees(int departmentId); // Not Allowed !!
您可以创建单独的类型作为违规核心类型的包装,以区分签名。
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);
答案 7 :(得分:2)
另一种选择是使用Query对象来构建“WHERE子句”。所以你只有一个像这样的方法:
public List<Product> GetProducts(Query query)
Query对象包含以面向对象的方式表达的condidition。 GetProducts通过“解析”Query对象来获取查询。
答案 8 :(得分:1)
是的,你可以过度使用它,但是这里有另一个概念可以帮助控制它的使用......
如果您使用.Net 3.5+并且需要应用多个过滤器,那么最好使用IQueryable和链接,即
GetQuery<Type>().ApplyCategoryFilter(category).ApplyProductNameFilter(productName);
通过这种方式,您可以在需要时重复使用过滤逻辑。
public static IQueryable<T> ApplyXYZFilter(this IQueryable<T> query, string filter)
{
return query.Where(XYZ => XYZ == filter);
}
答案 9 :(得分:1)
当你在方法的参数中只有细微的差别时,我看到过度使用过度使用了。例如:
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 }
在我的小例子中,很快就不清楚第二个int
参数是否应该是所有者或客户ID。
答案 10 :(得分:1)
对框架的简要介绍应该说服你,许多重载是一种公认的事态。面对无数的重载,Microsoft框架设计指南的第5.1.1节(Kwalina和Abrams,2006)直接解决了可用性重载的设计。以下是该部分的简要介绍:
DO 尝试使用描述性参数名称来指示较短重载所使用的默认值。
避免在重载中任意改变参数名称。
AVOID 在重载成员的参数排序方面存在不一致。
DO 仅使最长的过载虚拟(如果需要可扩展性)。更短的过载应该简单地调用更长的过载。
请勿使用ref
或out
参数来重载成员。
DO 允许null
传递可选参数。
DO 使用成员重载而不是使用默认参数定义成员。
答案 11 :(得分:0)
您可以根据需要使用重载。 从最佳实践的角度来看,如果您尝试对数据执行相同的“操作”(整体),建议您使用重载。例如。 getProduct()
此外,如果您看到Java API,则无处不在进行重载。你不会找到比这更大的认可。
答案 12 :(得分:0)
重载是理想的多态行为。它有助于人类程序员记住方法名称。如果explicit对于type参数是多余的,那么它就是坏的。如果type参数并不意味着该方法正在做什么,那么显式开始有意义。
在您的示例中,getProductByName是唯一可能有意义的情况,因为您可能希望通过其他字符串获取产品。这个问题是由原始类型的模糊性造成的;在某些情况下,getProduct(Name n)可能是更好的重载解决方案。
答案 13 :(得分:0)
如果不是这种情况并且所有三个都返回几个(如果你传递了它产品ID 5,它返回productid中任何带有5的项目,或者返回名称中带有字符串参数的任何项目)然后我会调用所有三个GetProducts或GetProductList并覆盖所有这些。
无论如何,名称应该反映函数的作用,因此在返回产品列表时调用GetProduct(singular)并不能成为一个好的函数名。 IMNSHO
答案 14 :(得分:0)
我完全忠于“显式”方式:为每个功能赋予不同的名称。我甚至重构了一些过去有Add(...)
个函数的代码,AddRecord(const Record&)
,AddCell(const Cell&)
等等。
我认为这有助于避免一些混乱,无意中的演员表(至少在C ++中)和编译器警告,并且它提高了清晰度。
在某些情况下,您可能需要其他策略。我还没遇到过。
答案 15 :(得分:0)
怎么样
public IList<Product> GetProducts() { /* Return all. */}
public IList<Product> GetProductBy(int productId) {...}
public IList<Product> GetProductBy(Category category) {...}
public IList<Product> GetProductBy(string Name ) {...}
等等?