Pergunta

O que há de melhor prática na definição de vários métodos que retornam a mesma forma de dados com filtros diferentes? método explícito nomes ou métodos sobrecarregados?

Por exemplo. Se eu tiver alguns produtos e eu estou puxando a partir de um banco de dados

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    }

forma sobrecarregada:

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 }

Eu sei que você pode entrar em um problema com assinaturas semelhantes , mas se você estiver passando objetos em vez de tipos base (String, int, char, DateTime, etc) isso vai ser menos de um questão. Então ... é uma boa idéia para sobrecarga de um método para reduzir o número de métodos que você tem e para maior clareza, ou deve cada método que filtra os dados de forma diferente ter um nome método diferente ?

Foi útil?

Solução

Sim, a sobrecarga pode facilmente ser usado em demasia.

Descobri que a chave para trabalhar fora se uma sobrecarga é justificada ou não é a de considerar o público - não o compilador, mas o programador de manutenção que será bem vinda em semanas / meses / anos e tem que entender o que o código está tentando alcançar.

Um nome de método simples como GetProducts () é clara e compreensível, mas deixa um não dito muito.

Em muitos casos, se o parâmetro passado para GetProducts () são bem nomeado, o cara de manutenção será capaz de descobrir o que a sobrecarga faz - mas isso é contando com boa disciplina nomeação no ponto de uso, que você pode' t aplicar. O que você pode impor é o nome do método que eles estão chamando.

A diretriz que eu sigo é apenas métodos de sobrecarga se eles são intercambiáveis ??- se eles fazem a mesma coisa. Dessa forma, eu não me importo qual a versão do consumidor dos meus invoca classe, como eles são equivalentes.

Para ilustrar, eu ficaria feliz em usar sobrecargas para uma DeleteFile () método:

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

No entanto, para os seus exemplos, eu usaria nomes separados:

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

Tendo os nomes completos torna o código mais explícito para a manutenção guy (que poderia muito bem ser eu). Isso evita problemas com ter colisões de assinatura:

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

Há também a oportunidade de introduzir sobrecarga para cada finalidade:

// 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 é lido muito mais do que está escrito - mesmo se você nunca voltar para o código após a inicial check-in para controle de origem, você ainda vai estar lendo essa linha de código um par de dúzia de vezes enquanto você escreve o código que se segue.

Por fim, a menos que você está escrevendo descartável código, você precisa permitir que outras pessoas chamando seu código de outras línguas. Parece que a maioria dos sistemas de negócios acabam por ficar na produção bem passado a sua utilização por data. Pode ser que o código que consome sua classe em 2016 acaba sendo escritos em VB.NET, C # 6.0, F # ou algo completamente novo que não foi inventado ainda. Pode ser que a linguagem não suportar sobrecargas.

Outras dicas

Tanto quanto eu posso dizer, você não vai ter menos métodos, apenas menos nomes. I geralmente preferem o sistema sobrecarregado método de nomeação, mas eu não acho que ele realmente faz muita diferença, desde que você comentar e documentar o seu código bem (o que você deve fazer em ambos os casos).

Você pode overuse-lo? Bem, sim, isso é verdade.

No entanto, os exemplos que você deu são exemplos perfeitos de quando usar a sobrecarga de método. Todos eles desempenham a mesma função, por que lhes dão nomes diferentes só porque você está passando tipos diferentes para eles.

A regra principal, está fazendo a mais clara, mais fácil de entender coisa. Não use a sobrecarga apenas para ser liso ou inteligente, fazê-lo quando faz sentido. Outros desenvolvedores pode estar trabalhando em este código também. Você quer torná-lo tão fácil como possível para eles para pegar e entender o código e ser capaz de implementar mudanças sem implementar bugs.

Eu gosto de sobrecarregar os meus métodos de modo que mais tarde no intellisense Eu não tenho um milhão do mesmo método. E parece mais lógico para mim ter apenas sobrecarregado, em vez de tê-lo chamado de forma diferente uma dúzia de vezes.

Uma coisa que você pode considerar é que você não pode expor métodos sobrecarregados como contratos de operação em um Web Service WCF. Então, se você acha que nunca pode precisar fazer isso, seria um argumento para o uso de diferentes nomes de método.

Outro argumento para diferentes nomes de métodos é que eles podem ser mais facilmente detectável usando intellisense.

Mas há prós e contras de uma ou outra escolha -. Todo o projeto é trade-off

Você provavelmente precisará de alguns padrões todo o projeto. Pessoalmente, acho que os métodos sobrecarregados muito mais fácil de ler. Se você tem o apoio IDE, vá para ele.

O ponto de sobrecarregar a facilitar a curva de aprendizagem de alguém que usa o seu código ... e permitem que você use nomeando esquemas que informam o usuário sobre o que o método faz.

Se você tem métodos diferentes dez que todos retornam uma coleção de funcionários, então gerar nomes de dez diferentes, (Especialmente se eles começam com letras diferentes!) Faz com que eles aparecem como múltiplas entradas em seus usuários intellisense cair para baixo, estendendo o comprimento do drop-down, e escondendo a distinção entre o conjunto de métodos dez que todos retorno uma coleção empregado, e quaisquer outros métodos são em sua classe ...

Pense sobre o que já é imposta pelo framework .Net para, digamos, construtores e indexadores ... Todos eles são obrigados a ter o mesmo nome, e você só pode criar múltiplos sobrecarregando-los ...

Se você sobrecarregá-los, todos eles vão aparecer como um, com suas assinaturas diferentes e comentários off para a reconstrução da casa lado.

Você não deve sobrecarregar dois métodos se eles executam funções diferentes ou não relacionados ...

Como a confusão que podem existir quando você quer sobrecarregar dois métodos com a mesma assinatura por tipo como em

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

Bem, você pode criar tipos distintos como invólucros para o tipo de núcleo ofensivo para distinguir as assinaturas ..

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

Outra opção é usar um objeto de consulta para construir a "cláusula WHERE". Então você teria apenas um método como este:

public List<Product> GetProducts(Query query)

A consulta objeto contém o condidition expressa de uma forma orientada a objetos. Os GetProducts obter a consulta por "analisar" o objeto de consulta.

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

sobrecarga

Eu tenho visto em demasia quando você tem apenas diferenças sutis nos argumentos para o método. Por exemplo:

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  }

Na minha pequeno exemplo, ele rapidamente se torna claro se o segundo argumento int deve ser o proprietário ou cliente id.

Um breve olhar para o quadro deve convencê-lo de que numerosos sobrecargas é um estado aceitável de assuntos. Em face de uma miríade de sobrecargas, o projeto de sobrecargas para usabilidade é directamente abordada pela secção 5.1.1 das Diretrizes de Design do Framework da Microsoft (Kwalina e Abrams, 2006). Aqui está um breve resumo do que a seção:

  • DO tentar usar nomes de parâmetro descritivos para indicar o padrão usado pelo sobrecargas menores.

  • EVITAR arbitrariamente variando nomes de parâmetro em sobrecargas.

  • EVITAR sendo inconsistente na ordenação de parâmetros em membros sobrecarregados.

  • DO fazer apenas a sobrecarga mais longa virtual (se for necessária a extensibilidade). sobrecargas mais curtos deve simplesmente chamar através de uma sobrecarga de mais tempo.

  • NÃO ref uso ou out parâmetros para membros de sobrecarga.

  • DO permitir null a ser passada para argumentos opcionais.

  • DO membro do uso sobrecarga em vez de definir membros com argumentos padrão.

Sim, você pode abusar da sua utilização, no entanto aqui é um outro conceito que pode ajudar a manter o uso de tudo sob controle ...

Se você estiver usando .Net 3.5+ e necessidade de aplicar vários filtros que são, provavelmente, melhor usar IQueryable e encadeamento ou seja

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

Dessa forma, você pode reutilizar a lógica de filtragem mais e mais sempre que você precisar.

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

Você pode usar Sobrecarga tanto quanto você quiser. Das melhores práticas ponto de vista, bem como, é recomendado que você use Sobrecarga se você está tentando executar a mesma "operação" (holística) sobre os dados. Por exemplo. getProduct ()

Além disso, se você vê API Java, Sobrecarga está em toda parte. Você não vai encontrar um endosso maior do que isso.

A sobrecarga é um comportamento polimórfico desejável. Ele ajuda o programador humano lembrar o nome do método. Se explícita é redundante com o parâmetro de tipo, então é ruim. Se o parâmetro de tipo não implica que o método está fazendo, em seguida, começa explícitas a fazer sentido.

No seu exemplo, getProductByName é o único caso em que explícita pode fazer sentido, já que você pode querer obter o produto por outra string. Este problema foi causado pela ambiguidade dos tipos de primitivas; getProduct (Nome n) pode ser uma solução melhor sobrecarga em alguns casos.

Sim, você pode abusar da sua utilização. No seu exemplo afigura-se como o primeiro eo terceiro seria provavelmente retornar um único item, onde o segundo voltaria vários. Se isso estiver correto, então eu chamaria o primeiro eo terceiro getProduct eo segundo GetProducts ou GetProductList

Se isto não é o caso e todos os três de retorno vários (como em se você passá-lo ProductID 5, ele retorna quaisquer itens com 5 na productid, ou retorna todos os itens com o parâmetro de cadeia em seu nome), então eu chamaria todos os três GetProducts ou GetProductList e substituir todos eles.

Em qualquer caso, o nome deve refletir o que a função faz, por isso, chamando-getProduct (singular), quando ele retorna uma lista de produtos não faz um bom nome função. IMNSHO

Eu sou um fã total do caminho "explícito": dando a cada função um nome diferente. Eu mesmo reformulado algum código que teve muito de funções Add(...) no passado, para AddRecord(const Record&), AddCell(const Cell&), etc.

Eu acho que isso ajuda a evitar algumas confusões, moldes inadvertidas (em C ++, pelo menos) e avisos do compilador, e melhora a clareza.

Talvez em alguns casos, você precisa da outra estratégia. Eu não encontrei ainda.

Como cerca

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

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

E assim por diante?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top