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 como Stream
(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 o Base
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 e Enumerable
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 isso Banana
, a abstração é totalmente sem sentido e você deve descartar a interface e usar apenas uma classe. Se houver um nome melhor (digamos, LongBanana
ou AppleBanana
), não há razão para não usar Banana
como o nome da interface. Portanto, o uso do I
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 o I
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 exemplo JsonParser
, 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 o I
prefixo-, mantenha-o. Faça uma anotação mental sempre que vir código class 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.