2008-10-29 11 views
35

¿Qué es mejor práctica al definir varios métodos que devuelven la misma forma de datos con diferentes filtros? ¿Nombres de métodos explícitos o métodos sobrecargados?Método de sobrecarga. ¿Puedes abusar de eso?

Por ejemplo. Si tengo algunos productos y estoy tirando de una base de datos

manera explícita:

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 } 

manera sobrecargada:

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 } 

Sabemos que es posible conseguir en un problema con firmas similares , pero si está pasando objetos en lugar de tipos básicos (cadena, int, char, DateTime, etc.), esto será un problema menor. Entonces ... ¿es una buena idea para sobrecargar un método para reducir el número de métodos que tiene y para mayor claridad, o debe cada método que filtra los datos de una manera diferente tener un nombre de método diferente?

Respuesta

43

Sí, la sobrecarga puede sobreutilizarse fácilmente.

He descubierto que la clave para determinar si una sobrecarga está justificada o no es considerar a la audiencia, no al compilador, sino al programador de mantenimiento que vendrá en semanas/meses/años y tiene que entender lo que el código está tratando de lograr

Un nombre de método simple como GetProducts() es claro y comprensible, pero no deja mucho que decir.

En muchos casos, si el parámetro pasado a GetProducts() está bien nombrado, el técnico de mantenimiento podrá determinar qué hace la sobrecarga, pero eso depende de una buena disciplina de nomenclatura en el punto de uso, que puede hacer cumplir. Lo que puede hacer cumplir es el nombre del método que están llamando.

La pauta que sigo es sobrecargar solo los métodos si son intercambiables, si hacen lo mismo. De esta forma, no me importa qué versión invoca el consumidor de mi clase, ya que son equivalentes.

Para ilustrar, con mucho gusto haría uso de sobrecargas por un método DeleteFile():

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

Sin embargo, para sus ejemplos, que haría uso de nombres distintos:

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

Tener la plena names hace que el código sea más explícito para el tipo de mantenimiento (que bien podría ser yo). Evita problemas con que tienen colisiones firma:

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

También existe la posibilidad de introducir una sobrecarga para cada propósito:

// 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) {...} 

Código es leer mucho más de lo que está escrito - incluso si nunca llega Volviendo al código después de la verificación inicial en el control de la fuente, todavía leerás esa línea de código un par de docenas de veces mientras escribes el código que sigue.

Por último, a menos que esté escribiendo código descartable, debe permitir que otras personas llamen a su código desde otros idiomas. Parece que la mayoría de los sistemas empresariales terminan estando en producción mucho más allá de su uso por fecha. Puede ser que el código que consume tu clase en 2016 termine escrito en VB.NET, C# 6.0, F # o algo completamente nuevo que aún no se haya inventado. Es posible que el idioma no admita sobrecargas.

+4

Publicación muy agradable con muchos ejemplos. ¡Gracias! – Armstrongest

+1

+1 ¡sí, publicación realmente útil, aplausos! – andy

+2

+1 excelente consejo, de acuerdo de todo corazón. La sobrecarga de métodos sin una necesidad real es malvada. Añadiría que (al menos en Java) es aún más malo en setters – leonbloy

16

Hasta donde puedo decir, no tendrá menos métodos, solo menos nombres. Por lo general, prefiero el método de nombres sobrecargado de nombres, pero no creo que realmente haga mucha diferencia, siempre y cuando comentes y documentes bien tu código (lo cual debes hacer en ambos casos).

1

Otra opción es usar un objeto Query para construir la "cláusula WHERE". Por lo que tendría sólo un método como este:

public List<Product> GetProducts(Query query) 

El objeto de consulta contiene la condidition expresado de una manera orientada a objetos. Los GetProducts obtienen la consulta "analizando" el objeto Query.

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

+0

Sin embargo, ¿qué harías en la capa donde no quieres exponer el hecho de que ya estás usando SQL? ¿O digamos que está consultando el DOM? –

+0

Este es un antipatrón clásico. – rmeador

+0

Creo que esto sería difícil de implementar con un Object Store como LinQ. Pero tengo curiosidad. – Armstrongest

4

es probable que tenga algunos estándares de todo el proyecto. Personalmente, encuentro que los métodos sobrecargados son mucho más fáciles de leer. Si tienes el soporte IDE, ve por ello.

11

Me gusta sobrecargar mis métodos para que más tarde en el intellisense no tenga un millón del mismo método. Y me parece más lógico tenerlo sobrecargado en vez de llamarlo de manera diferente una docena de veces.

15

¿Se puede abusar? bueno, sí, eso es verdad

Sin embargo, los ejemplos que ha proporcionado son ejemplos perfectos de cuándo usar la sobrecarga de métodos. Todos realizan la misma función, ¿por qué darles nombres diferentes solo porque les pasas tipos diferentes?

La regla principal es hacer lo más claro y fácil de entender. No use la sobrecarga solo para ser astuto o inteligente, hágalo cuando tenga sentido. Otros desarrolladores también podrían estar trabajando en este código. Desea hacer que sea lo más fácil posible que ellos recojan y comprendan el código y sean capaces de implementar cambios sin implementar errores.

1

Sí se puede abusar de ella, sin embargo aquí es otro concepto que podría ayudar a mantener el uso de la misma bajo control ...

Si está utilizando .Net 3.5 + y la necesidad de aplicar varios filtros que son probablemente mejores usar IQueryable y encadenamiento, es decir,

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

De esta manera puede reutilizar la lógica de filtrado una y otra vez donde sea que la necesite.

public static IQueryable<T> ApplyXYZFilter(this IQueryable<T> query, string filter) 
{ 
    return query.Where(XYZ => XYZ == filter); 
} 
+0

Esto es fascinante. En mi implementación, tengo artículos, ArticleCategory, ArticleSubCategory, etc. en Linq DataContext Class. Cada método actualmente llamado un Proc almacenado diferente. Realmente necesito profundizar en la interfaz IQueryable para entenderlo. – Armstrongest

+0

La ejecución diferida de Linq es la clave de cómo funciona :) Me tomó un tiempo entender los métodos IQueryable, Generics y de extensión, pero tengo que decir que ahora disfruto de la programación más que nunca. – JTew

1

He visto sobrecarga excesiva cuando solo tiene sutiles diferencias en los argumentos del método. Por ejemplo:

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 } 

En mi pequeño ejemplo, se convierte rápidamente en claro si el segundo argumento int debe ser el propietario o N ° de cliente.

+0

y es por eso que tiene que poner comentarios muy claros (o Javadoc, si esto es Java, que parece ser) en el código para que el próximo desarrollador pueda resolverlo. – Elie

+0

Esto es cierto, y es lo que estaba tratando de evitar. Es por eso que creo que necesito enviar objetos en lugar de CategoryIds ... – Armstrongest

+0

No, solo tenga mucho cuidado de que el código y los comentarios estén de acuerdo ... y asegúrese de que los comentarios sean muy claros ... – Elie

5

Una cosa que puede considerar es que no puede exponer los métodos sobrecargados como contratos de operación en un servicio web de WCF. Entonces, si crees que alguna vez necesitarás hacer esto, sería un argumento para usar diferentes nombres de métodos.

Otro argumento para nombres de métodos diferentes es que pueden ser más fácilmente detectables usando intellisense.

Pero existen ventajas y desventajas para cualquiera de las dos opciones: todo diseño es una solución de compromiso.

4

El punto de sobrecarga para facilitar la curva de aprendizaje de alguien que usa su código ... y para permitirle usar esquemas de nombres que informan al usuario sobre lo que hace el método.

Si tiene diez métodos diferentes que devuelven una colección de empleados, generar diez nombres diferentes (¡especialmente si comienzan con letras diferentes!) Hace que aparezcan como entradas múltiples en el menú desplegable intellisense de los usuarios, que se extiende la longitud del menú desplegable, y ocultando la distinción entre el conjunto de diez métodos que devuelven una colección de empleados, y cualquier otro método que esté en su clase ...

Piense en lo que ya hace cumplir la plataforma .Net para, digamos constructores, e indexadores ... Todos están obligados a tener el mismo nombre, y solo puedes crear múltiplos sobrecargándolos ...

If yo Si los sobrecarga, todos aparecerán como uno, con sus firmas y comentarios dispares en el costado.

No se debe sobrecargar dos métodos si realizan funciones diferentes o no relacionados ...

En cuanto a la confusión que puede existir cuando se quiere sobrecargar dos métodos con la misma firma por tipo como en

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

Bien puede crear tipos separados como contenedores para el tipo de núcleo infractor distinguir las firmas ..

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); 
+0

¡Esta es una GRAN solución! ¡Gracias! Hay muchos buenos comentarios aquí. ¡Quiero elegir más de uno como una buena solución! Este es definitivamente uno de ellos. – Armstrongest

+0

Puede ser en algunos casos, pero agrega complejidad. NO abusar de esto. Envolver todos sus ID int en estructuras no es una buena solución ... –

0

Puedes usar Sobrecarga todo lo que quieras. También desde el punto de vista de las mejores prácticas, se recomienda que use Sobrecarga si está tratando de realizar la misma "operación" (holísticamente) en los datos. P.ej. getProduct()

Además, si ve la API de Java, la sobrecarga está en todas partes. No encontrarías un respaldo mayor que eso.

1

Un breve vistazo al marco debe convencerlo de que numerosas sobrecargas son un estado aceptado.Frente a una miríada de sobrecargas, el diseño de sobrecargas para la usabilidad se aborda directamente en la sección 5.1.1 de las Pautas de diseño de Microsoft Framework (Kwalina y Abrams, 2006). He aquí una breve Precis de esa sección:

  • DO intenta utilizar nombres de los parámetros descriptivos para indicar el valor por defecto utilizado por sobrecargas más cortos.

  • EVITAR cambiar arbitrariamente los nombres de parámetros en sobrecargas.

  • EVITAR siendo inconsistente en el orden de los parámetros en los miembros sobrecargados.

  • DO hacen solo la sobrecarga más larga virtual (si se requiere extensibilidad). Las sobrecargas más cortas deberían simplemente solicitar una sobrecarga más larga.

  • NOref uso o out parámetros sobrecargar miembros.

  • DO permiten null que se pasan los argumentos opcionales.

  • DO usan sobrecarga de miembros en lugar de definir miembros con argumentos predeterminados.

0

La sobrecarga es un comportamiento polimórfico deseable. Ayuda al programador humano a recordar el nombre del método. Si explícito es redundante con el parámetro tipo, entonces es malo. Si el parámetro tipo no implica lo que el método está haciendo, entonces explícito comienza a tener sentido.

En su ejemplo, getProductByName es el único caso donde explícito podría tener sentido, ya que es posible que desee obtener el producto por alguna otra cadena. Este problema fue causado por la ambigüedad de los tipos primitivos; getProduct (Name n) podría ser una mejor solución de sobrecarga en algunos casos.

0

sí, puede abusar de él. En su ejemplo, parecería que el primero y el tercero probablemente devolverían un solo artículo, mientras que el segundo devolvería varios. Si eso es correcto, llamaría al primero y al tercer GetProduct y al segundo GetProducts o GetProductList

si este no es el caso y los tres devuelven varios (como si lo pasara productID 5, devuelve cualquier elemento con 5 en el productid, o devuelve cualquier elemento con el parámetro de cadena en su nombre), entonces llamaría a los tres GetProducts o GetProductList y los anularía a todos.

En cualquier caso, el nombre debe reflejar lo que hace la función, por lo que llamarlo GetProduct (singular) cuando devuelve una lista de Productos no tiene un buen nombre de función. IMNSHO

0

Soy un fanático total de la forma "explícita": darle a cada función un nombre diferente. Incluso he refactorizado algún código que tenía un montón de Add(...) funciones en el pasado, a AddRecord(const Record&), AddCell(const Cell&), etc.

Creo que esto ayuda a evitar algunas confusiones, yesos involuntarios (en C++, por lo menos) y las advertencias del compilador, y mejora la claridad.

Quizás en algunos casos necesite la otra estrategia. No he encontrado uno todavía.

0

¿Qué tal

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

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

Y así sucesivamente?

Cuestiones relacionadas