É suficiente distinguir métodos apenas pelo nome do argumento (não pelo tipo)?


36

É suficiente distinguir métodos apenas pelo nome do argumento (não pelo tipo) ou é melhor nomeá-lo mais explicitamente?

Por exemplo T Find<T>(int id)vs T FindById<T>(int id).

Existe alguma boa razão para nomeá-lo de forma mais explícita (por exemplo, adicionar ById) vs manter apenas o nome do argumento?

Uma razão pela qual consigo pensar é quando as assinaturas dos métodos são iguais, mas têm um significado diferente.

FindByFirstName(string name) e FindByLastName(string name)


4
Então, quando você sobrecarrega o Find para incluir T Find<T>(string name)ou (int size)como planeja resolver os problemas inevitáveis?
UKMonkey

3
@UKMonkey que problemas inevitáveis?
Konrad

3
no primeiro caso: se várias entradas tiverem o mesmo nome, você precisará alterar a assinatura da função; o que significa que as pessoas provavelmente ficarão confusas com o que deve retornar; No último caso, o argumento é o mesmo - e, portanto, uma sobrecarga ilegal. Você começa a nomear a função com "byX" ou cria um objeto para o argumento para poder ter o equivalente a sobrecarga com o mesmo argumento. Ou funciona bem para diferentes situações.
UKMonkey

2
@UKMonkey você pode enviar uma resposta com alguns exemplos de código se você quiser
Konrad

3
Os IDs provavelmente devem ser um IDobjeto opaco e não apenas um int. Dessa forma, obtenha uma verificação em tempo de compilação de que você não usa um ID para int ou vice-versa em alguma parte do seu código. E com isso você pode ter find(int value)e find(ID id).
Bakuriu 26/11/18

Respostas:


68

Certamente, há uma boa razão para nomeá-lo mais explicitamente.

Não é principalmente a definição do método que deve ser auto-explicativa, mas o uso do método . E embora findById(string id)e find(string id)sejam ambos auto-explicativos, há uma enorme diferença entre findById("BOB")e find("BOB"). No primeiro caso, você sabe que o literal aleatório é, de fato, um ID. No último caso, você não tem certeza - pode realmente ser um nome ou algo completamente diferente.


9
Salvo nos 99% de outros casos onde você tem um nome de variável ou propriedade é um ponto de referência: findById(id)vs find(id). Você pode ir de qualquer maneira nisso.
precisa

16
@ GregBurghardt: Um valor específico não é necessariamente nomeado da mesma maneira para um método e seu chamador. Por exemplo, considere double Divide(int numerator, int denominator)a ser utilizado em um método: double murdersPerCapita = Divide(murderCount, citizenCount). Você não pode contar com os dois métodos com o mesmo nome da variável como há uma abundância de casos em que não é o caso (ou quando é o caso, é ruim de nomenclatura)
Flater

1
@Flater: Dado o código da pergunta (encontrando coisas de algum tipo de armazenamento persistente), imagino que você possa chamar esse método com um "assassinedCitizenId" ou "citizenId" ... Eu realmente não acho que os argumentos ou nomes de variáveis ​​sejam ambígua aqui. E honestamente, eu poderia ir de qualquer maneira nisso. Na verdade, é uma pergunta muito opinativa.
precisa

4
@ GregBurghardt: Você não pode destilar uma regra global a partir de um único exemplo. A pergunta do OP é em geral , não específica ao exemplo dado. Sim, há alguns casos em que usar o mesmo nome faz sentido, mas também há casos em que não. Portanto, essa resposta é melhor procurar consistência, mesmo que não seja necessária em um subconjunto de casos de uso.
Flater

1
Os parâmetros de nomes resolvem a confusão após a confusão já existir , conforme você precisa examinar a definição do método, enquanto os métodos explicitamente nomeados evitam completamente a confusão , tendo o nome presente na chamada do método. Além disso, você não pode ter dois métodos com o mesmo nome e tipo de argumento, mas com um nome diferente, o que significa que você precisará de nomes explícitos em certos casos.
Darkhogg

36

Vantagens de FindById () .

  1. Futuro à prova : Se você começar com Find(int), e mais tarde tem que adicionar outros métodos ( FindByName(string), FindByLegacyId(int), FindByCustomerId(int), FindByOrderId(int), etc), pessoas como eu tendem a gastar idades procurando FindById(int). Não é realmente um problema se você puder e mudará Find(int)para FindById(int)quando necessário - a prova futura é sobre estes se s.

  2. Mais fácil de ler . Findestá perfeitamente bem se a chamada parecer record = Find(customerId);Ainda FindByIdé um pouco mais fácil de ler se estiver record = FindById(AFunction());.

  3. Consistência . Você pode aplicar consistentemente o padrão FindByX(int)/ em FindByY(int)qualquer lugar, mas Find(int X)/ Find(int Y)não é possível porque eles entram em conflito.

Vantagens de Find ()

  • BEIJO. Findé simples e direto e, ao lado operator[]dele, é um dos 2 nomes de funções mais esperados nesse contexto. (Algumas alternativas populares sendo get, lookupoufetch , dependendo do contexto).
  • Como regra geral, se você tiver um nome de função que seja uma única palavra conhecida que descreva com precisão o que a função faz, use-a. Mesmo se houver um nome mais longo com várias palavras, é um pouco melhor para descrever o que a função faz. Exemplo: Length vs NumberOfElements . Há uma troca, e onde traçar a linha está sujeito a um debate em andamento.
  • Geralmente é bom evitar redundância. Se olharmos FindById(int id), podemos remover facilmente a redundância alterando-a para Find(int id), mas há uma troca - perdemos alguma clareza.

Como alternativa, você pode obter as vantagens de ambos usando IDs fortemente tipados:

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

Implementação de Id<>: Digitando fortemente valores de ID em C #

Os comentários aqui, bem como no link acima, levantaram várias preocupações sobre as Id<Customer>quais gostaria de abordar:

  • Preocupação 1: É um abuso de genéricos. CustomerIde OrderIDsão tipos diferentes ( customerId1 = customerId2;=> bom, customerId1 = orderId1;=> ruim), mas sua implementação é quase idêntica, para que possamos implementá-los com copiar colar ou com metaprogramação. Embora exista valor em uma discussão sobre expor ou ocultar o genérico, a metaprogramação é para que servem os genéricos.
  • Preocupação 2: não impede erros simples. / É uma solução em busca de um problema O principal problema que é removido usando Ids fortemente tipados é a ordem errada dos argumentos em uma chamada para DoSomething(int customerId, int orderId, int productId). Ids fortemente tipados também evitam outros problemas, incluindo o que o OP perguntou.
  • Preocupação 3: Ele realmente apenas obscurece o código. É difícil dizer se um ID é mantido int aVariable. É fácil dizer que um ID está retido Id<Customer> aVariable, e podemos até dizer que é um ID de cliente.
  • Preocupação 4: esses IDs não são tipos fortes, apenas invólucros. Stringé apenas um invólucro byte[]. O agrupamento ou encapsulamento não está em conflito com a digitação forte.
  • Preocupação 5: acabou de engenharia. Aqui está a versão mínima, embora eu recomende adicionar operator==e operator!=também, se você não quiser confiar exclusivamente em Equals:

.

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}

10

Outra maneira de pensar sobre isso é usar o tipo de segurança da linguagem.

Você pode implementar um método como:

Find(FirstName name);

Onde FirstName é um objeto simples que envolve uma string que contém o primeiro nome e significa que não pode haver confusão quanto ao que o método está fazendo, nem nos argumentos com os quais é chamado.


4
Não tenho certeza qual é a sua resposta para a pergunta dos OPs. Você recomenda o uso de um nome como "Localizar", contando com o tipo de argumento? Ou você recomenda usar esses nomes somente quando houver um tipo explícito para o (s) argumento (s) e usar um nome mais explícito como "FindById" em outro lugar? Ou você recomenda introduzir tipos explícitos para tornar um nome como "Localizar" mais viável?
Doc Brown

2
@ DocBrown Eu acho que o último, e eu gosto. Na verdade, é semelhante à resposta de Pedro, iiuc. A lógica que eu entendo é dupla: (1) do tipo de argumento fica claro o que a função faz; (2) Você não pode cometer erros como., O string name = "Smith"; findById(name);que é possível se você usar tipos gerais não descritos.
Peter - Restabelece Monica

5
Embora, em geral, eu seja fã da segurança do tipo tempo de compilação, tenha cuidado com os tipos de invólucro. A introdução de classes de wrapper para fins de segurança de tipo pode, às vezes, complicar bastante sua API se for feita em excesso. por exemplo, todo o problema do "artista anteriormente conhecido como int" que o winapi tem; a longo prazo, eu diria que a maioria das pessoas apenas olha para os intermináveis DWORD LPCSTRclones etc. e pensa "é um int / string / etc.", chega ao ponto em que você passa mais tempo sustentando suas ferramentas do que realmente criando código .
JRH

1
@jrh True. Meu teste decisivo para a introdução de tipos "nominais" (diferindo apenas no nome) é quando funções / métodos comuns não fazem sentido em nosso caso de uso, por exemplo, ints são frequentemente somados, multiplicados etc., o que não faz sentido para IDs; então eu faria IDdiferente int. Isso pode simplificar uma API, restringindo o que podemos fazer com um valor (por exemplo, se tivermos um ID, ele só funcionará com find, não por exemplo, ageou highscore). Por outro lado, se nos encontrarmos convertendo muito ou escrevendo a mesma função / método (por exemplo find) para vários tipos, isso é um sinal de que nossas distinções são muito boas #
Warbo

1

Vou votar em declarações explícitas como FindByID .... O software deve ser criado para o Change. Deve estar aberto e fechado (SOLID). Portanto, a classe está aberta para adicionar um método de localização semelhante, como, por exemplo, FindByName ... etc.

Mas FindByID está fechado e sua implementação é testada em unidade.

Não vou sugerir métodos com predicados, eles são bons em nível genérico. E se, com base no campo (ByID), você concluir uma metodologia diferente.


0

Estou surpreso que ninguém tenha sugerido usar um predicado como o seguinte:

User Find(Predicate<User> predicate)

Com essa abordagem, você não apenas reduz a superfície da sua API, mas também dá mais controle ao usuário que a usa.

Se isso não for suficiente, você sempre poderá expandi-lo para suas necessidades.


1
O problema é que ele é menos eficiente devido ao fato de não poder tirar proveito de coisas como índices.
Solomon Ucko
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.