Devemos renomear métodos sobrecarregados?


14

Suponha uma interface contendo estes métodos:

Car find(long id);

List<Car> find(String model);

É melhor renomeá-los assim?

Car findById(long id);

List findByModel(String model);

De fato, qualquer desenvolvedor que use essa API não precisará procurar na interface para conhecer os possíveis argumentos dos find()métodos iniciais .

Portanto, minha pergunta é mais geral: qual é o benefício de usar métodos sobrecarregados no código, pois isso reduz a legibilidade?


4
Qualquer um dos métodos é aceitável desde que você seja consistente.
ChrisF

Existe uma relação entre sobrecarregar e substituir um método. No entanto, este artigo favorece a sua sugestão - Pode ser de seu interesse: roseindia.net/javatutorials/…
NoChance 29/12/12

Respostas:


23

Esse é um problema relativamente pequeno em comparação com muitas outras práticas ruins de legibilidade às quais você pode estar suscetível, então eu diria que é principalmente uma questão de gosto como você nomeia seus métodos.

Com isso dito, se você quiser fazer algo sobre isso, eu seguiria esta prática:

  • Sobrecarga se ...

    Os métodos obedecem quase ao mesmo contrato, mas simplesmente operam com informações diferentes (imagine uma operadora de telefone que possa procurar sua conta pelo seu número de identificação fiscal pessoal, número da sua conta ou seu nome e data de nascimento). Isso inclui retornar o mesmo tipo de saída .

  • Use um nome diferente se ...

    Os métodos fazem coisas substancialmente diferentes ou retornam resultados diferentes (como o seu caso). Você pode considerar usar um nome diferente se um acessar o banco de dados e outro não.

    Além disso, se o tipo retornado for diferente, eu também alteraria o verbo para indicar que:

    Car findById(long id);
    
    List findAllByModel(String model);
    

3
FWIW, eu diria que um pouco mais fortemente: "Os métodos obedecem exatamente ao mesmo contrato ..." Ou seja, o tipo / contagem de argumentos não importa - a semântica da chamada de função é idêntica independentemente. Se o tipo / contagem de argumentos for importante, você não deve sobrecarregar.
Mcmcc

4

Eu recomendaria usar um nome diferente, em todos os casos. É possível que, em algum momento no futuro, você queira adicionar outro método, digamos List<Car> findByMake(String make), em contraste com List<Car> findByModel(String model). Então, de repente, chamar tudo findpara de fazer sentido. Seus métodos também têm menos probabilidade de serem utilizados incorretamente incorretamente, se seus nomes fornecerem mais informações sobre como eles devem ser usados.


1
Para ser justo, isso não seria um problema se a funcionalidade fosse representada mais explicitamente por objetos: find(Make val)e find(Model val). Então, métodos de conveniência como findByMake(String val)seria muito mais claro o que eles estão realmente fazendo. Afinal, a Stringnão é uma marca ou modelo, portanto o método deve explicar o que realmente está fazendo.
Nicole

4

Se você renomear um método, ele não será mais sobrecarregado. Por si só, sobrecarregar não necessariamente torna o código menos legível, mas pode dificultar a implementação se a sintaxe não estiver clara.

Muitos idiomas usam a sobrecarga de método como um meio de apresentar uma interface para a funcionalidade em que os parâmetros podem ser opcionais e os padrões para os parâmetros opcionais estão implícitos. Isso é particularmente verdadeiro para idiomas que não suportam uma sintaxe de parâmetro padrão na declaração do método.

Então, fazendo isso:

void MyMethod(int param1, int param2 = 10)
{
    ...
}

evita que você faça isso:

void MyMethod(int param1)
{
    MyMethod(param1, Param2Default);
}

void MyMethod(int param1, int param2)
{
    ....
}

Quanto ao que é mais legível, isso realmente se resume a você. Pessoalmente, prefiro a segunda opção, principalmente quando a lista de parâmetros está ficando um pouco longa, mas suponho que isso realmente não importa, desde que você seja consistente em toda a sua API.

A dificuldade com sobrecarga ocorre quando você deseja funções que fazem essencialmente a mesma coisa e onde deseja que a lista de parâmetros seja a mesma, mas os tipos de retorno sejam diferentes. A maioria dos idiomas não sabe diferenciar entre dois métodos com o mesmo nome, mas com tipos de retorno diferentes. Neste ponto, você precisa pensar em usar genéricos, alterar a interface do parâmetro ou renomear um dos seus métodos para indicar a diferença no tipo de retorno. É aqui que a legibilidade pode se tornar um grande problema, se você não optar por um esquema de nomeação simples e claro para lidar com situações como essa.

Nomear seus métodos sobrecarregados GetSomething()e GetSomethingEx()não vai dizer muito sobre quais são as diferenças entre seus métodos, principalmente se os tipos de retorno são as únicas diferenças entre eles. Por outro lado, GetSomethingAsInt()e GetSomethingAsString()conte um pouco mais sobre o que os métodos estão fazendo e, embora não sejam estritamente uma sobrecarga, indique que os dois métodos fazem coisas semelhantes, mas retornam tipos de valores diferentes. Eu sei que existem outras maneiras pelas quais você pode nomear métodos, no entanto, para fins de ilustração do argumento, esses exemplos grosseiros devem servir.

No exemplo dos OPs, a renomeação não é estritamente necessária porque os parâmetros do método são diferentes; no entanto, torna as coisas um pouco mais claras para nomear um método mais especificamente. No final, tudo se resume ao tipo de interface que você deseja apresentar aos seus usuários. A decisão de não sobrecarregar não deve ser tomada apenas com base em sua própria percepção de legibilidade. Os métodos de sobrecarga podem, por exemplo, simplificar uma interface de API e reduzir o número de métodos que um desenvolvedor pode precisar se lembrar; por outro lado, pode ofuscar a interface em um grau que exige que o desenvolvedor leia a documentação do método para entender qual formulário do método a ser usado, enquanto ter vários métodos com nomes semelhantes e descritivos pode torná-lo mais aparente apenas lendo um nome de método quanto à sua finalidade.


0

Favorecer a sobrecarga, desde que os métodos retornem a mesma coisa e sigam o mesmo contrato. A sobrecarga libera o código de chamada de confirmar desnecessariamente o tipo de parâmetro.

Suponha que a função de chamada receba uma consulta de pesquisa como parâmetro e execute algum outro processamento antes e / ou depois da chamada para find.

void tryToSellCars(String which) {
    /* grab an airhorn, inflatable tube guy... */
    List<Car> cars = find(which);
    /* expound virtues of each car in detail... */
}

Se você deseja alterar o tipo dessa consulta por qualquer motivo no futuro (por exemplo, de uma cadeia de ID simples para um objeto de consulta completo de algum tipo), você pode fazer essa alteração na função de chamada apenas alterando a assinatura da função para aceitar o novo tipo de parâmetro sem se preocupar em alterar o método que ele chama em sua classe.

void tryToSellCar(CarQuery which) {
    /* grab airhorn, inflate tube guy... */
    List<Car> cars = find(which)
    /* expound virtues of each car in detail... */
}

Se você implementar findByIde findByQueryObjectseparadamente, você teria que caçar cada chamada para fazer essa mudança. No exemplo, mudei apenas uma palavra e terminei.


Esta resposta assume, é claro, que você está usando uma linguagem que suporta sobrecarga com erros em tempo de compilação para um parâmetro inválido. Se você estiver escrevendo JavaScript ou Ruby ou qualquer outra linguagem que não suporte nativamente a sobrecarga, eu sempre usaria o verboso findByFoopara detectar incompatibilidades de tipo anteriormente.
Sqykly 29/10
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.