O capítulo do tio Bob sobre nomes no Código Limpo recomenda que você evite codificações em nomes, principalmente em relação à notação húngara. Ele também menciona especificamente a remoção do I
prefixo das interfaces, mas não mostra exemplos disso.
Vamos assumir o seguinte:
- O uso da interface é principalmente para obter testabilidade através da injeção de dependência
- Em muitos casos, isso leva a ter uma única interface com um único implementador
Então, por exemplo, como devem ser nomeados esses dois? Parser
e ConcreteParser
? Parser
e ParserImplementation
?
public interface IParser {
string Parse(string content);
string Parse(FileInfo path);
}
public class Parser : IParser {
// Implementations
}
Ou devo ignorar essa sugestão em casos de implementação única como essa?
c#
naming
clean-code
naming-standards
Vinko Vrsalovic
fonte
fonte
Respostas:
Enquanto muitos, incluindo o "tio Bob", recomendam não usar
I
como prefixo para interfaces, isso é uma tradição bem estabelecida com o C #. Em termos gerais, deve ser evitado. Mas se você estiver escrevendo C #, realmente deve seguir as convenções desse idioma e usá-las. Não fazer isso causará grande confusão com qualquer pessoa familiarizada com C # que tente ler seu código.fonte
A interface é o conceito lógico importante, portanto, a interface deve conter o nome genérico. Então, eu prefiro ter
do que
Este último tem vários problemas:
fonte
Default
ouReal
ouImpl
em tantos dos meus nomes de classeIList<T>
e realmente não se importa como é armazenado internamente; ser capaz de apenas removerI
e ter um nome de classe utilizável parece melhor do que ter que saber magicamente (como em Java) que o código que deseja uma implementação comumList<T>
deve ser usadoArrayList<T>
.CFoo : Foo
. Portanto, essa opção não está realmente disponível. Dois, você tem uma inconsistência estranho, em seguida, porque algumas classes teria o marcador e outros se não, dependendo se há pode haver outras implementações da mesma interface.CFoo : Foo
masSqlStore : Store
. Esse é um problema que tenhoImpl
, que é essencialmente apenas um marcador mais feio. Talvez se houvesse uma linguagem com a convenção de sempre adicionando umC
(ou qualquer outro), que pode ser melhor do queI
Não se trata apenas de convenções de nomenclatura. O C # não suporta herança múltipla, portanto, esse uso herdado da notação húngara tem um pequeno benefício, embora útil, em que você está herdando de uma classe base e implementando uma ou mais interfaces. Então, é isso...
... é preferível a isso ...
IMO
fonte
inherits
vsimplements
.extends
.Não não não.
As convenções de nomenclatura não são uma função dos seus fãs do tio Bob. Eles não são uma função da linguagem, c # ou de outra forma.
Eles são uma função da sua base de código. Em uma extensão muito menor de sua loja. Em outras palavras, o primeiro na base de código define o padrão.
Só então você decide. Depois disso, apenas seja consistente. Se alguém começar a ser inconsistente, tire sua arma nerf e faça com que atualizem a documentação até que se arrependam.
Ao ler uma nova base de código, se eu nunca
I
vir um prefixo, estou bem, independentemente do idioma. Se eu vejo um em cada interface, estou bem. Se às vezes vejo uma, alguém vai pagar.Se você se encontra no contexto rarefeito de definir esse precedente para sua base de código, recomendo que você considere isso:
Como classe cliente, reservo-me o direito de não dar a mínima para o que estou falando. Tudo o que preciso é de um nome e uma lista de coisas que eu possa chamar contra esse nome. Isso pode não mudar, a menos que eu diga. Eu possuo essa parte. O que estou falando, interface ou não, não é meu departamento.
Se você esgueirar-se
I
com esse nome, o cliente não se importará. Se não houverI
, o cliente não se importa. O que está falando pode ser concreto. Talvez não. Como não o chamanew
de qualquer maneira, não faz diferença. Como cliente, eu não sei. Eu não quero saberAgora, como um macaco de código olhando para esse código, mesmo em java, isso pode ser importante. Afinal, se eu tiver que alterar uma implementação para uma interface, posso fazer isso sem informar o cliente. Se não houver
I
tradição, talvez nem o renomeie. O que pode ser um problema, porque agora precisamos recompilar o cliente, porque, embora a fonte não se importe com o binário, Algo fácil de perder se você nunca mudou o nome.Talvez isso não seja um problema para você. Talvez não. Se for, é uma boa razão para se importar. Mas, pelo amor aos brinquedos de mesa, não afirme que devemos fazer isso cegamente, porque é assim que é feito em algum idioma. Ou tem uma razão muito boa ou não se importa.
Não se trata de viver com isso para sempre. É sobre não me dar a mínima para a base de código não estar de acordo com sua mentalidade em particular, a menos que você esteja preparado para corrigir o 'problema' em todos os lugares. A menos que você tenha uma boa razão para não seguir o caminho de menor resistência à uniformidade, não venha a mim pregando a conformidade. Eu tenho coisas melhores para fazer.
fonte
I
s. Se o seu código os usar, você os verá em todos os lugares. Se o seu código não os estiver usando, você os verá no código da estrutura, levando exatamente ao mix que você não deseja.Há uma pequena diferença entre Java e C # que é relevante aqui. Em Java, cada membro é virtual por padrão. Em C #, todos os membros são selados por padrão - exceto os membros da interface.
As suposições que acompanham isso influenciam a diretriz - em Java, todo tipo público deve ser considerado não final, de acordo com o Princípio de Substituição de Liskov [1]. Se você tiver apenas uma implementação, nomeará a classe
Parser
; se você achar que precisa de várias implementações, altere a classe para uma interface com o mesmo nome e renomeie a implementação concreta para algo descritivo.Em C #, a principal suposição é que, quando você obtém uma classe (o nome não começa com
I
), essa é a classe que você deseja. Lembre-se, isso não chega nem perto de 100% de precisão - um contra-exemplo típico seria de classes comoStream
(que realmente deveriam ter sido uma interface ou algumas interfaces), e todo mundo tem suas próprias diretrizes e experiências de outros idiomas. Há também outras exceções, como oBase
sufixo bastante usado para denotar uma classe abstrata - assim como em uma interface, você sabe que o tipo deve ser polimórfico.Há também um bom recurso de usabilidade ao deixar o nome sem prefixo I para a funcionalidade relacionada a essa interface sem ter que recorrer a transformar a interface em uma classe abstrata (o que prejudicaria devido à falta de herança múltipla de classe em C #). Isso foi popularizado pelo LINQ, que usa
IEnumerable<T>
como interface eEnumerable
como repositório de métodos que se aplicam a essa interface. Isso é desnecessário em Java, onde as interfaces também podem conter implementações de métodos.Por fim, o
I
prefixo é amplamente usado no mundo C # e, por extensão, no mundo .NET (como a maioria do código .NET é escrita em C #, faz sentido seguir as diretrizes C # para a maioria das interfaces públicas). Isso significa que você quase certamente estará trabalhando com bibliotecas e códigos que seguem essa notação, e faz sentido adotar a tradição para evitar confusões desnecessárias - não é como omitir o prefixo que tornará seu código melhor :)Suponho que o raciocínio do tio Bob fosse algo assim:
IBanana
é a noção abstrata de banana. Se pode haver qualquer classe de implementação que não teria um nome melhor do que issoBanana
, a abstração é totalmente sem sentido e você deve descartar a interface e usar apenas uma classe. Se houver um nome melhor (digamos,LongBanana
ouAppleBanana
), não há razão para não usarBanana
como o nome da interface. Portanto, o uso doI
prefixo significa que você possui uma abstração inútil, o que dificulta a compreensão do código sem nenhum benefício. E como o OOP estrito fará com que você sempre codifique contra interfaces, o único lugar onde você não veria oI
prefixo em um tipo seria em um construtor - ruído bastante inútil.Se você aplicar isso à sua
IParser
interface de amostra , poderá ver claramente que a abstração está inteiramente no território "sem sentido". Ou há algo específico sobre a implementação concreta de um analisador (por exemploJsonParser
,XmlParser
...), ou você deve usar apenas uma classe. Não existe "implementação padrão" (embora em alguns ambientes isso realmente faça sentido - principalmente COM), existe uma implementação específica ou você deseja uma classe abstrata ou métodos de extensão para os "padrões". No entanto, em C #, a menos que sua base de código já omita oI
prefixo-, mantenha-o. Faça uma anotação mental sempre que vir códigoclass Something: ISomething
- isso significa que alguém não é muito bom em seguir o YAGNI e criar abstrações razoáveis.[1] - Tecnicamente, isso não é mencionado especificamente no artigo de Liskov, mas é um dos fundamentos do artigo original da POO e, na minha leitura de Liskov, ela não contestou isso. Em uma interpretação menos rigorosa (a adotada pela maioria das linguagens OOP), isso significa que qualquer código que utilize um tipo público destinado a substituição (por exemplo, não final / selado) deve funcionar com qualquer implementação em conformidade desse tipo.
fonte
Em um mundo ideal, você não deve prefixar seu nome com uma mão curta ("I ..."), mas simplesmente deixar o nome expressar um conceito.
Eu li em algum lugar (e não posso obtê-lo) a opinião de que esta convenção ISomething leva você a definir um conceito "IDollar" quando você precisa de uma classe "Dollar", mas a solução correta seria um conceito chamado simplesmente "Moeda".
Em outras palavras, a convenção dá uma saída fácil à dificuldade de nomear as coisas e a saída fácil está sutilmente errada .
No mundo real, os clientes do seu código (que podem ser apenas "você do futuro") precisam ser capazes de lê-lo mais tarde e familiarizá-lo o mais rápido (ou com o mínimo de esforço) possível (fornecer aos clientes do seu código o que eles esperam obter).
A convenção ISomething apresenta nomes de cruft / bad. Dito isto, o cruft não é real *), a menos que as convenções e práticas usadas na escrita do código não sejam mais reais; se a convenção diz "use ISomething", é melhor usá-lo, em conformidade (e funcionar melhor) com outros desenvolvedores.
*) Posso dizer que isso porque "cruft" não é um conceito exato de qualquer maneira :)
fonte
Como as outras respostas mencionam, prefixar seus nomes de interface
I
faz parte das diretrizes de codificação da estrutura .NET. Como as classes concretas são mais frequentemente interagidas do que as interfaces genéricas em C # e VB.NET, elas são de primeira classe em esquemas de nomenclatura e, portanto, devem ter os nomes mais simples - portanto,IParser
para a interface eParser
a implementação padrão.Porém, no caso de Java e linguagens similares, onde as interfaces são preferidas em vez de classes concretas, o Tio Bob está certo em que
I
deve ser removido e as interfaces devem ter os nomes mais simples -Parser
para a interface do analisador, por exemplo. Mas, em vez de um nome baseado em funçãoParserDefault
, a classe deve ter um nome que descreva como o analisador é implementado :Este é, por exemplo, como
Set<T>
,HashSet<T>
, eTreeSet<T>
são nomeados.fonte
sealed
por padrão e você deve fazê-los explicitamentevirtual
. Ser virtual é o caso especial em C #. Obviamente, como a abordagem recomendada para escrever código mudou ao longo dos anos, muitas relíquias do passado precisavam permanecer para compatibilidade :) O ponto principal é que, se você obtiver uma interface em C # (que começa comI
), você saberá que é totalmente tipo abstrato; se você obtiver uma não interface, a suposição típica é que esse é o tipo que você deseja, que o LSP seja condenado.Você está certo de que esta convenção I = interface quebra as (novas) regras
Antigamente, você prefixava tudo com seu tipo intCounter boolFlag etc.
Se você quiser nomear coisas sem o prefixo, mas também evitar 'ConcreteParser', poderá usar namespaces
fonte
intCounter
ouboolFlag
. Esses são os "tipos" errados. Em vez disso, você escreveriapszName
(ponteiro para uma sequência terminada em NUL contendo um nome),cchName
(contagem de caracteres na sequência "nome"),xControl
(coordenada x do controle),fVisible
(sinalizador indicando que algo está visível),pFile
(ponteiro para um arquivo),hWindow
(identificador para uma janela) e assim por diante.xControl
acchName
. Ambos são do tipoint
, mas possuem semânticas muito diferentes. Os prefixos tornam essas semânticas óbvias para um humano. Um ponteiro para uma string terminada em NUL se parece exatamente com um ponteiro para uma string no estilo Pascal para um compilador. Da mesma forma, oI
prefixo para interfaces surgiu na linguagem C ++, na qual interfaces são realmente apenas classes (e, na verdade, para C, onde não existe classe ou interface no nível da linguagem, é apenas uma convenção).