Padrão vs Impl ao implementar interfaces em Java

34

Após a leitura Os nomes dos pacotes devem ser singular ou plural? Ocorreu-me que nunca vi um debate adequado cobrindo uma das minhas irritações: nomear implementações de interfaces.

Vamos supor que você tenha uma interface Orderque deve ser implementada de várias maneiras, mas existe apenas a implementação inicial quando o projeto é criado. Você procura DefaultOrderou OrderImplou alguma outra variante para evitar a falsa dicotomia? E o que você faz quando mais implementações aparecem?

E o mais importante ... por quê?

Gary Rowe
fonte

Respostas:

59

Os nomes têm a oportunidade de transmitir significado. Por que você jogaria fora essa oportunidade com o Impl?

Primeiro de tudo, se você tiver apenas uma implementação, elimine a interface. Ele cria esse problema de nomenclatura e não adiciona nada. Pior ainda, pode causar problemas com assinaturas de método inconsistentes nas APIs se você e todos os outros desenvolvedores não tiverem o cuidado de sempre usar apenas a interface.

Dado isso, podemos assumir que toda interface possui ou pode ter duas ou mais implementações.

  • Se você possui apenas um agora e não sabe de que maneira o outro pode ser diferente, o padrão é um bom começo.

  • Se você tem dois agora, nomeie cada um de acordo com sua finalidade.

    Exemplo: Recentemente, tivemos uma classe concreta Context (em referência a um banco de dados). Percebemos que precisávamos representar um contexto offline, portanto o nome Contexto foi usado para uma nova interface (para manter a compatibilidade com APIs antigas) e uma nova implementação foi criada, OfflineContext . Mas adivinhe como o original foi renomeado? É isso mesmo, ContextImpl (caramba).

    Nesse caso, o DefaultContext provavelmente ficaria bem e as pessoas o receberiam, mas não é tão descritivo quanto poderia ser. Afinal, se não estiver offline , o que é? Então fomos com: OnlineContext .


Caso especial: Usando o prefixo "I" nas interfaces

Uma das outras respostas sugeridas usando o prefixo I nas interfaces. De preferência, você não precisa fazer isso.

No entanto, se você precisar de uma interface, para implementações personalizadas, mas também tiver uma implementação concreta primária que será usada com freqüência, e o nome básico para ela é simples demais para desistir de uma interface sozinha, considere adicionar "I" para a interface (no entanto, está tudo bem se ele ainda não estiver adequado para você e sua equipe).

Exemplo: Muitos objetos podem ser um "EventDispatcher". Por uma questão de APIs, isso deve estar em conformidade com uma interface. Mas você também deseja fornecer um distribuidor de eventos básico para delegação. DefaultEventDispatcher seria bom, mas é um pouco longo, e se você vir o nome com frequência, pode preferir usar o nome base EventDispatcher para a classe concreta e implementar o IEventDispatcher para implementações personalizadas:

/* Option 1, traditional verbose naming: */
interface EventDispatcher { /* interface for all event dispatchers */ }
class DefaultEventDispatcher implements EventDispatcher {
  /* default event dispatcher */
}

/* Option 2, "I" abbreviation because "EventDispatcher" will be a common default: */
interface IEventDispatcher { /* interface for all event dispatchers */ }
class EventDispatcher implements IEventDispatcher {
  /* default event dispatcher. */
}
Nicole
fonte
3
+1 para uma resposta sólida. Como o raciocínio por trás de não usar o Impl.
Gary Rowe
2
+1 concordo absolutamente. Nomear uma interface para longe do conceito de domínio que ela representa está totalmente errado.
rupjones
9
"se você terá apenas uma implementação", como você sabe de antemão que terá apenas uma implementação? "toda interface tem ou pode ter duas ou mais implementações" ... antes de ter duas implementações, você primeiro não tem, a você tem uma, então você tem duas, quero dizer que pode haver um momento em que você tenha apenas uma implementação, apenas antes da implementação do segundo.
Tulains Córdova
2
@NickC Eu não estou sendo um pedante sobre semântica (sou poeta e nem sabia disso). O inglês não é minha língua materna, por isso não posso ser pedante. Eu estava falando sobre lógica defeituosa. Você usa interfaces para desacoplar. Isso não requer um certo número de implementações.
Tulains Córdova
4
if you will only ever have one implementation, do away with the interface- a menos que você queira testar o componente; nesse caso, convém manter essa interface para criar MockOrder, OrderStub ou similar.
JBWilkinson
15

Eu decido a nomeação pelo caso de uso da interface.

Se a interface for usada para dissociação , eu escolho as Implimplementações.

Se o objetivo da interface é abstração comportamental , as implementações são nomeadas de acordo com o que estão fazendo concretamente. Costumo acrescentar o nome da interface a isso. Então, se a interface for chamada Validator, eu uso FooValidator.

Acho que Defaulté uma escolha muito ruim. Primeiro, polui os recursos de conclusão de código, porque os nomes sempre começam com ele. A outra coisa é que um padrão está sujeito a alterações ao longo do tempo. Portanto, o que primeiro pode ser um padrão pode, depois de algum tempo, ser um recurso obsoleto. Portanto, você sempre começa a renomear suas classes assim que os padrões mudam ou vive com nomes enganosos.

SpaceTrucker
fonte
8

Eu concordo com a resposta de Nicole (particularmente que a interface provavelmente não é necessária na maioria dos casos), mas para fins de discussão, lançarei uma alternativa adicional além de OrderImple DefaultOrder: ocultar a implementação por trás de um método de fábrica estático como Orders.create(). Por exemplo:

public final class Orders {
  public static Order create() {
    return new Order() {
      // Implementation goes here.
    };
  }
}

Com essa abordagem, a implementação pode ser uma classe interna anônima ou uma classe privada com DefaultouImpl no nome, ou pode ser nomeada outra coisa completamente. Qualquer que seja a escolha feita, o chamador não precisa se importar, para que você obtenha mais flexibilidade agora e mais tarde quando / se você decidir alterá-la.

Alguns ótimos exemplos desse padrão na prática são as classes utilidade java.util.Collectionse java.util.concurrent.Executors, cujos métodos retornam implementações ocultas. Como o Java Efetivo menciona (no Item 1), esse padrão pode ajudar a manter o "peso conceitual" da API menor.

Andrew McNamee
fonte
+1 para um caso adicional interessante: implementações anônimas.
Gary Rowe
1
Também usado extensivamente na goiaba .
30713 Nicole
3

Eu sempre uso OrderImplsimplesmente porque aparece em ordem alfabética logo após a Orderinterface.

Ben Hoffstein
fonte
2
E como você lida com as implementações adicionais em andamento, para tratamento especial e assim por diante?
Gary Rowe
1
Você perguntou sobre a implementação inicial. Depois de iniciar a implementação de DomesticOrder, ForeignOrder ou qualquer outra coisa, eles são nomeados com mais atenção e sem levar em consideração sua posição no alfabeto.
Ben Hoffstein
Muito bem - editei a pergunta original para refletir esse novo requisito.
Gary Rowe
Não é uma boa ideia Se houver mais de uma implementação.
Sadegh
0

Você pode nomear a interface com o prefixo I (IWhatever) e fazer a implementação Whatever.

As convenções oficiais de código Java não falam sobre esse tipo de nomeação de interfaces, no entanto, esse tipo de nomeação ajuda no reconhecimento e na navegação.

Matthieu
fonte
Por que você prefixaria a interface com um I? É para ajudar no reconhecimento na navegação?
Gary Rowe
@ Gary: prefixar os tipos de interface com I é uma convenção bem estabelecida na linguagem Delphi. No Delphi, os tipos de classe são prefixados com T. Portanto, teríamos uma interface IOrder e uma implementação padrão TOrder com TSomethingOrder e TBullMarketOrder como implementações específicas.
Marjan Venema
3
Eu já vi essa convenção ser muito usada no desenvolvimento do .NET. Talvez porque a documentação e os exemplos da Microsoft a usem.
Ben Hoffstein
5
Parece que o @Renesis apresentou um caso útil para usá-lo em Java. Pessoalmente, eu confiaria no IDE para me dizer a diferença, caso contrário teríamos E's para Enums, C's para classes e todos em grande parte redundantes.
Gary Rowe
0

Eu acho que, embora o Default possa fazer sentido em alguns casos, seria mais útil descrever a implementação. Portanto, se sua interface é UserProfileDAOentão suas implementações podem ser UserProfileSQLDAOou UserProfileLDAPDAOou algo parecido.

jiggy
fonte
0

se possível, nomeie-o após o que / como ele funciona.

normalmente, a pior abordagem é dar um nome a ela como deve ser usada.

Se ele deve ser usado como classe base para implementação, você pode usar o BaseX ou o AbstractX (se for abstrato (mas tente adiantar o que faz, porque se ele não fez nada, você não o criaria, você já terá Se fornecer a funcionalidade mais simples possível e se espera que ela seja usada diretamente (sem estendê-la) quando essa funcionalidade for suficiente, você poderá chamá-lo de SimpleX ou BasicX.

Se for usado, a menos que outra implementação seja fornecida, nomeie-o DefaultX

user470365
fonte
-2

A maioria dessas respostas descreve o que está sendo feito, mas não o porquê.

O que aconteceu com os princípios de OO? O que o Impl me diz sobre uma aula e como usá-la? Você deve se preocupar em entender os usos e não como eles são construídos.

Se eu criar uma interface Person, o que é um PersonImpl? Sem significado. Pelo menos DefaultPerson me diz quem quem o codificou não deveria ter criado uma interface.

As interfaces estão digitando polimorfismo e devem ser usadas como tal.

user108620
fonte
1
A resposta aceita do @NickC entra em alguns detalhes sobre o que está sendo feito e por quê.
Gary Rowe