Existe uma pequena diferença entre Java e C # que é relevante aqui. Em Java, cada membro é virtual por padrão. Em C #, cada membro é lacrado por padrão - exceto para 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, você nomeará a classe
Parser
; Se você achar que precisa de múltiplas implementações, você apenas mudará a classe para uma interface com o mesmo nome e renomeará a implementação concreta para algo descritivo.
Em C #, a principal suposição é que, quando você recebe uma classe (o nome não começa com I
), essa é a classe que você deseja. Lembre-se, isso está longe de ser 100% preciso - um contra-exemplo típico seria classes como Stream
(que realmente deveria ter sido uma interface, ou algumas interfaces), e todo mundo tem suas próprias diretrizes e origens de outros idiomas . Há também outras exceções, como o sufixo Base
amplamente utilizado para denotar uma classe abstrata - assim como com uma interface, você sabe que o tipo deve ser polimórfico.
Há também um bom recurso de usabilidade ao deixar o nome não-prefixado para funcionalidade que se relaciona com essa interface sem ter que recorrer a tornar a interface 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 um repositório de métodos que se aplicam a essa interface. Isso é desnecessário em Java, onde as interfaces podem conter implementações de métodos também.
Por fim, o prefixo I
é amplamente usado no mundo C # e, por extensão, no mundo .NET (já que a maior parte do código .NET é escrito em C #, faz sentido seguir as diretrizes C # para a maioria dos as 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ão desnecessária - não é como omitir o prefixo tornará seu código melhor:)
Assumo que o raciocínio do tio Bob foi algo assim:
IBanana
é a noção abstrata de banana. Se pode haver qualquer classe de implementação que não teria um nome melhor que Banana
, a abstração é totalmente sem sentido, e você deve abandonar a interface e usar apenas uma classe. Se houver um nome melhor (por exemplo, LongBanana
ou AppleBanana
), não há motivo para não usar Banana
como o nome da interface. Portanto, usar o prefixo I
significa que você tem uma abstração inútil, o que dificulta o entendimento do código sem nenhum benefício. E como a OOP restrita faz com que você sempre codifique em interfaces, o único lugar onde você não veria o prefixo I
em um tipo estaria em um construtor - um ruído sem sentido.
Se você aplicar isso à sua interface IParser
de amostra, poderá ver claramente que a abstração está inteiramente no território "sem sentido". Há algo específico sobre uma implementação concreta de um analisador (por exemplo, JsonParser
, XmlParser
, ...) ou você deve usar apenas uma classe. Não existe uma "implementação padrão" (embora em alguns ambientes, isso realmente faça sentido - notavelmente, COM), ou há 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
-prefix, mantenha-o. Apenas faça uma anotação mental toda vez que você vir o código como class Something: ISomething
- isso significa que alguém não é muito bom em seguir o YAGNI e construir abstrações razoáveis.
[1] - Tecnicamente, isso não é especificamente mencionado no artigo de Liskov, mas é uma das fundações do original OOP paper e na minha leitura de Liskov, ela não contestou isso. Em uma interpretação menos estrita (a adotada pela maioria das linguagens OOP), isso significa que qualquer código que use um tipo público destinado a substituição (ou seja, não final / selado) deve funcionar com qualquer implementação conforme desse tipo.