Prefixos e sufixos não pertencem aos nomes de interface
O uso do prefixo I
ou do sufixo Interface
em
interfaces, bem como Abstract
em classes abstratas, é um
antipadrão. Não tem lugar em código limpo. A diferenciação dos nomes de
interface, na verdade, obscurece os princípios da OOP, introduz ruído no
código e causa complicações durante o desenvolvimento. As razões são as
seguintes.

Tipo = classe + interface + descendentes
No mundo da OOP, tanto classes quanto interfaces são consideradas tipos. Se eu usar um tipo na declaração de uma propriedade ou parâmetro, do ponto de vista do desenvolvedor, não há diferença se o tipo em que ele se baseia é uma classe ou uma interface. Isso é ótimo, e é o que torna as interfaces tão úteis. Isso dá sentido à sua existência. (Sério: para que serviriam as interfaces se este princípio não fosse válido? Tente pensar sobre isso.)
Veja este código:
class FormRenderer
{
public function __construct(
private Form $form,
private Translator $translator,
) {
}
}
O construtor diz: “Preciso de um formulário e um tradutor.” E
não importa se ele recebe um objeto GettextTranslator
ou
DatabaseTranslator
. E, ao mesmo tempo, como usuário, não me
importa se Translator
é uma interface, uma classe abstrata ou uma
classe concreta.
Não me importa mesmo? Na verdade não, confesso que sou bastante curioso, então quando estou explorando uma biblioteca de terceiros, dou uma olhada no que está por trás do tipo e passo o mouse sobre ele:

Ah, agora eu sei. E isso termina aqui. O conhecimento se é uma classe ou interface seria importante se eu quisesse criar sua instância, mas não é o caso, agora estou apenas falando sobre o tipo da variável. E aqui quero estar protegido desses detalhes. E certamente não quero introduzi-los no meu código! O que está por trás do tipo faz parte de sua definição, não do tipo em si.
E agora veja o próximo código:
class FormRenderer
{
public function __construct(
private AbstractForm $form,
private TranslatorInterface $translator,
) {
}
}
Esta definição do construtor diz literalmente: “Preciso de um formulário abstrato e uma interface de tradutor.” Mas isso é um disparate. Ele precisa de um formulário concreto para renderizar. Não um formulário abstrato. E precisa de um objeto que desempenhe o papel de tradutor. Não precisa de uma interface.
Você sabe que as palavras Interface
e Abstract
devem ser ignoradas. Que o construtor quer o mesmo que no exemplo anterior.
Mas… sério? Você realmente acha uma boa ideia introduzir o uso de palavras
em suas convenções de nomenclatura que devem ser ignoradas?
Isso cria uma falsa impressão sobre os princípios da OOP. Um iniciante deve
ficar confuso: “Se o tipo Translator
significa 1) um objeto da
classe Translator
, 2) um objeto que implementa a interface
Translator
ou 3) um objeto que herda deles, o que então significa
TranslatorInterface
?” Não há resposta razoável para isso.
Quando escrevemos TranslatorInterface
, embora
Translator
também possa ser uma interface, cometemos uma
tautologia. O mesmo quando declaramos
interface TranslatorInterface
. E assim por diante. Até que surja
uma piada de programador:
interface TranslatorInterface
{
}
class FormRendererClass
{
/**
* Construtor
*/
public function __construct(
private AbstractForm $privatePropertyForm,
private TranslatorInterface $privatePropertyTranslator,
) {
// 🤷♂️
}
}
Implementação excepcional
Quando vejo algo como TranslatorInterface
, é provável que
também exista uma implementação com o nome
Translator implements TranslatorInterface
. Isso me faz pensar:
o que torna Translator
tão especial a ponto de ter o direito
exclusivo de se chamar Translator? Qualquer outra implementação precisa de um
nome descritivo, por exemplo, GettextTranslator
ou
DatabaseTranslator
, mas esta é de alguma forma “padrão”, como
sugere sua posição privilegiada, ao se chamar Translator
sem
adjetivo.
Isso até deixa as pessoas inseguras e sem saber se devem escrever
o typehint para Translator
ou TranslatorInterface
. No
código cliente, ambos acabam se misturando, certamente você já se deparou com
isso muitas vezes (no Nette, por exemplo, em conexão com
Nette\Http\Request
vs IRequest
).
Não seria melhor se livrar da implementação excepcional e manter o nome
genérico Translator
para a interface? Ou seja, ter
implementações concretas com nomes concretos + uma interface genérica com um
nome genérico. Isso faz sentido, afinal.
O fardo do nome descritivo então recai puramente sobre as implementações.
Se renomearmos TranslatorInterface
para Translator
,
nossa antiga classe Translator
precisa de um novo nome. As pessoas
tendem a resolver esse problema chamando-a de DefaultTranslator
, eu
também sou culpado disso. Mas, novamente, o que a torna tão excepcional a
ponto de se chamar Default? Não seja preguiçoso e pense cuidadosamente sobre
o que ela faz e por que difere de outras implementações possíveis.
E se eu não conseguir imaginar múltiplas implementações? E se apenas uma maneira válida me ocorrer? Então simplesmente não crie a interface. Pelo menos por enquanto.
Eis que surge outra implementação
E aqui está! Precisamos de uma segunda implementação. Isso acontece comumente. Nunca houve a necessidade de armazenar traduções de outra forma senão uma comprovada, por exemplo, em um banco de dados, mas agora surgiu um novo requisito e é necessário ter mais tradutores na aplicação.
Este também é o momento em que você percebe claramente qual era a especificidade do tradutor único original. Era um tradutor de banco de dados, nenhum padrão.
O que fazer?
- Transformamos o nome
Translator
em uma interface - Renomeamos a classe original para
DatabaseTranslator
e ela implementaráTranslator
- E criamos novas classes
GettextTranslator
e talvezNeonTranslator
Todas essas mudanças são feitas de forma muito confortável e fácil,
especialmente se a aplicação for construída de acordo com os princípios da
injeção
de dependência. Não é necessário alterar nada no código, apenas
na configuração do contêiner de DI, mudamos Translator
para
DatabaseTranslator
. Isso é ótimo!
Uma situação diametralmente diferente ocorreria, no entanto, se
insistíssemos em prefixar/sufixar. Teríamos que renomear os tipos de
Translator
para TranslatorInterface
em todo o código
da aplicação. Tal renomeação seria puramente proposital para cumprir a
convenção, mas iria contra o sentido da OOP, como mostramos há pouco.
A interface não mudou, o código do usuário não mudou, mas a convenção
exige renomear? Então é uma convenção errada.
Além disso, se com o tempo se mostrasse que uma classe abstrata seria melhor do que uma interface, renomearíamos novamente. Tal intervenção pode não ser trivial, por exemplo, se o código estiver distribuído em vários pacotes ou for usado por terceiros.
Mas todo mundo faz assim
Nem todo mundo. É verdade que no mundo PHP, a diferenciação de nomes de interfaces e classes abstratas foi popularizada pelo Zend Framework e depois pelo Symfony, ou seja, grandes players. Essa abordagem foi adotada também pela PSR, que paradoxalmente publica apenas interfaces e, ainda assim, inclui a palavra interface no nome de cada uma.
Por outro lado, outro framework significativo, o Laravel, não diferencia
interfaces e classes abstratas de forma alguma. Nem a popular camada de banco de
dados Doctrine faz isso. E nem a biblioteca padrão do PHP faz isso (temos
interfaces Throwable
ou Iterator
, classe abstrata
FilterIterator
, etc.).
Se olharmos para o mundo fora do PHP, por exemplo, C# usa o prefixo
I
para interfaces, enquanto em Java ou TypeScript os nomes não
são diferenciados.
Portanto, nem todo mundo faz isso, mas mesmo que fizessem, não significa que seja bom. Adotar sem pensar o que os outros fazem não é sensato, porque você pode adotar também os erros. Erros dos quais o outro talvez gostaria muito de se livrar, mas é muito difícil.
Não consigo distinguir no código o que é uma interface
Vários programadores argumentarão que os prefixos/sufixos são úteis para eles, pois graças a eles reconhecem imediatamente no código o que são interfaces. Eles sentem que sentiriam falta dessa distinção. Vamos ver, você consegue distinguir nestes exemplos o que é classe e o que é interface?
$o = new X;
class X extends X implements Y
{}
interface Y
{}
X::fn();
X::$v;
X
é sempre uma classe, Y
é uma interface, é
inequívoco mesmo sem prefixos/postfixos. Claro, o IDE também sabe disso e no
contexto dado sempre fornecerá as sugestões corretas.
Mas e aqui:
function foo(A $param): A
{}
public A $property;
$o instanceof A
A::CONST
try { ... } catch (A $x) { ... }
Nestes casos, você não consegue distinguir. Como dissemos no início, aqui não deve haver diferença do ponto de vista do desenvolvedor entre o que é uma classe e o que é uma interface. O que justamente dá sentido às interfaces e classes abstratas.
Se você fosse capaz de distinguir classe de interface aqui, isso negaria um princípio fundamental da OOP. E as interfaces perderiam o sentido.
Estou acostumado com isso
Mudar hábitos simplesmente dói 🙂 Às vezes, até a ideia. Mas, para não sermos injustos, muitas pessoas, pelo contrário, são atraídas por mudanças e as aguardam com expectativa, mas para a maioria, o hábito é uma camisa de ferro.
Mas basta olhar para o passado, como alguns hábitos foram levados pelo
tempo. Provavelmente a mais famosa é a chamada notação húngara usada desde
os anos oitenta e popularizada pela Microsoft. A notação consistia em que
o nome de cada variável começasse com uma abreviação simbolizando seu tipo
de dados. Em PHP, ficaria assim echo $this->strName
ou
$this->intCount++
. A notação húngara começou a ser
abandonada nos anos noventa e hoje a Microsoft em suas diretrizes desencoraja
diretamente os desenvolvedores de usá-la.
Antigamente era indispensável e hoje ninguém sente falta.
Mas por que ir tão longe no passado? Talvez você se lembre que em PHP era costume distinguir membros não públicos de classes com um sublinhado (exemplo do Zend Framework). Isso foi na época em que o PHP 5 já existia há muito tempo, com modificadores de visibilidade public/protected/private. Mas os programadores faziam isso por hábito. Estavam convencidos de que sem os sublinhados deixariam de se orientar no código. “Como eu distinguiria no código variáveis públicas de privadas, hein?”
Hoje ninguém usa sublinhados. E ninguém sente falta deles. O tempo provou brilhantemente que os receios eram infundados.
No entanto, é exatamente o mesmo que a objeção: “Como eu distinguiria no código interfaces de classes, hein?”
Eu parei de usar prefixos/postfixos há dez anos. Nunca voltaria atrás, foi uma ótima decisão. Também não conheço nenhum outro programador que gostaria de voltar. Como disse um amigo: “Experimente e em um mês você não entenderá como um dia fez diferente.”
Quero manter a consistência
Consigo imaginar um programador dizendo: “Usar prefixos e sufixos é realmente um disparate, eu entendo, mas já tenho meu código construído assim e a mudança é muito difícil. E se eu começasse a escrever o novo código corretamente sem eles, criaria uma inconsistência, que talvez seja ainda pior do que uma convenção ruim.”
Na verdade, seu código já é inconsistente agora, porque você usa a biblioteca do sistema PHP, que não tem prefixos nem postfixos:
class Collection implements ArrayAccess, Countable, IteratorAggregate
{
public function add(string|Stringable $item): void
{
}
}
E, sinceramente, isso incomoda? Já lhe ocorreu que seria mais consistente assim?
class Collection implements ArrayAccessInterface, CountableInterface, IteratorAggregateInterface
{
public function add(string|StringableInterface $item): void
{
}
}
Ou isto?
try {
$command = $this->find($name);
} catch (ThrowableInterface $e) {
return $e->getMessage();
}
Acho que não. A consistência não desempenha um papel tão significativo quanto poderia parecer. Pelo contrário, o olho prefere menos ruído visual, o cérebro a clareza do design. Portanto, ajustar a convenção e começar a escrever novas interfaces corretamente sem prefixos e sufixos faz sentido.
É possível removê-los propositalmente mesmo de grandes projetos. Um
exemplo é o Nette Framework, que historicamente usava prefixos I
nos nomes das interfaces, dos quais começou a se livrar gradualmente
e com total preservação da compatibilidade retroativa há
alguns anos.
Faça o login para enviar um comentário