Pregunta

¿Cuál es la 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?

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

forma 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 }

Me doy cuenta de que puede tener un problema con firmas similares , pero si está pasando objetos en lugar de tipos base (cadena, int, char, DateTime, etc.), esto será menos de una problema. Entonces ... es una buena idea sobrecargar un método para reducir la cantidad de métodos que tiene y, para mayor claridad, o deben cada método que filtra los datos de una manera diferente tiene un nombre de método diferente ?

¿Fue útil?

Solución

Sí, la sobrecarga se puede usar fácilmente en exceso.

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

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

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

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

Para ilustrar, me encantaría usar sobrecargas para un método DeleteFile ():

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

Sin embargo, para sus ejemplos, usaría nombres separados:

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

Tener los nombres completos hace que el código sea más explícito para el encargado del mantenimiento (que bien podría ser yo). Evita problemas por tener colisiones de firmas:

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

El código se lee mucho más de lo que se escribe, incluso si nunca vuelve al código después de la verificación inicial en el control de origen, seguirá leyendo esa línea de código un par de docenas de veces mientras escribe el código que sigue.

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

Otros consejos

Por lo que puedo decir, no tendrás menos métodos, solo menos nombres. En general, prefiero el sistema de nombres sobrecargado, pero no creo que realmente haga mucha diferencia siempre que comente y documente bien su código (lo que debería hacer en cualquier caso).

¿Puedes abusar de él? Bueno, sí, eso es cierto.

Sin embargo, los ejemplos que has dado son ejemplos perfectos de cuándo usar la sobrecarga de métodos. Todos realizan la misma función, por eso les dan nombres diferentes solo porque les estás pasando diferentes tipos.

La regla principal es hacer las cosas más claras y fáciles de entender. No utilice la sobrecarga para ser hábil o inteligente, hágalo cuando tenga sentido. Otros desarrolladores también podrían estar trabajando en este código. Desea que sea lo más fácil posible para ellos recoger y entender el código y poder implementar cambios sin implementar errores.

Me gusta sobrecargar mis métodos para que más adelante en el intellisense no tenga un millón del mismo método. Y me parece más lógico que esté sobrecargado en lugar de que tenga un nombre diferente una docena de veces.

Una cosa que puede considerar es que no puede exponer métodos sobrecargados como contratos de operación en un servicio web 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 diferentes nombres de métodos es que pueden ser más fáciles de descubrir usando intellisense.

Pero hay ventajas y desventajas para cualquiera de las opciones: todo diseño es una compensación.

Probablemente necesite algunos estándares para todo el proyecto. Personalmente, encuentro que los métodos sobrecargados son mucho más fáciles de leer. Si tienes el soporte IDE, hazlo.

El punto de sobrecargar para facilitar la curva de aprendizaje de alguien que usa su código ... y 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, luego generar diez nombres diferentes (¡especialmente si comienzan con letras diferentes!) hace que aparezcan como entradas múltiples en el menú desplegable intellisense de sus usuarios, extendiendo el 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 aplica el marco .Net para, digamos constructores e indexadores ... Todos están obligados a tener el mismo nombre, y solo puede crear múltiplos sobrecargándolos ...

Si los sobrecarga, aparecerán todos como uno, con sus firmas y comentarios dispares a un lado.

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

En cuanto a la confusión que puede existir cuando desea 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 !!

Bueno, puede crear tipos separados como envoltorios para el tipo de núcleo ofensivo para 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);

Otra opción es usar un objeto de consulta para compilar la " Cláusula WHERE " Entonces solo tendrías un método como este:

public List<Product> GetProducts(Query query)

El objeto de consulta contiene la condición expresada de forma orientada a objetos. Los GetProducts obtienen la consulta " análisis " El objeto de consulta.

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

He visto una sobrecarga en exceso cuando solo tienes diferencias sutiles 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, rápidamente no queda claro si el segundo argumento int debería ser el ID del propietario o del cliente.

Una breve mirada al marco debería convencerlo de que numerosas sobrecargas son un estado de cosas aceptado. Ante la gran cantidad de sobrecargas, el diseño de sobrecargas para uso se aborda directamente en la sección 5.1.1 de las Pautas de diseño de Microsoft Framework (Kwalina y Abrams, 2006). Aquí hay un breve resumen de esa sección:

  • DEBE intentar usar nombres de parámetros descriptivos para indicar el valor predeterminado utilizado por sobrecargas más cortas.

  • EVITA nombres de parámetros que varíen arbitrariamente en sobrecargas.

  • EVITA ser inconsistente en el orden de los parámetros en miembros sobrecargados.

  • HACER hacer que la sobrecarga más larga sea virtual (si se requiere extensibilidad). Las sobrecargas más cortas deberían simplemente llamar a una sobrecarga más larga.

  • NO use los parámetros ref o out para sobrecargar a los miembros.

  • DO permite que se pase null para argumentos opcionales.

  • DEBE usar la sobrecarga de miembros en lugar de definir a los miembros con argumentos predeterminados.

Sí, puedes usarlo en exceso, sin embargo, aquí hay otro concepto que podría ayudar a mantener su uso bajo control ...

Si está utilizando .Net 3.5+ y necesita aplicar varios filtros, probablemente sea mejor usar IQueryable y el encadenamiento, es decir,

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

De esta forma, puede reutilizar la lógica de filtrado una y otra vez donde la necesite.

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

Puedes usar la sobrecarga tanto como quieras. También desde el punto de vista de las mejores prácticas, se recomienda que utilice la sobrecarga si está intentando realizar la misma operación " " " (holisticamente) en los datos. P.ej. getProduct ()

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

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 de tipo, entonces es malo. Si el parámetro tipo no implica lo que está haciendo el método, entonces lo explícito comienza a tener sentido.

En su ejemplo, getProductByName es el único caso en el que 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.

sí, puedes usarlo en exceso. 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 primer y tercer GetProduct y al segundo GetProducts o GetProductList

si este no es el caso y los tres devuelven varios (como en si le pasas productID 5, devuelve cualquier ítem con 5 en productid, o devuelve cualquier ítem con el parámetro de cadena en su nombre), entonces llamaría los tres GetProducts o GetProductList y los anula 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 es un buen nombre de función. IMNSHO

Soy un fanático total de lo "explícito" manera: dar a cada función un nombre diferente. Incluso he refactorizado un código que tenía muchas funciones Add (...) en el pasado, a AddRecord (const Record & amp;) , AddCell (const Cell & amp;) , etc.

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

Quizás en algunos casos necesites la otra estrategia. Todavía no he encontrado uno.

¿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?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top