Predeterminado vs Impl al implementar interfaces en Java

34

Después de leer ¿Los nombres de los paquetes deben ser singulares o plurales? Se me ocurrió que nunca había visto un debate adecuado que cubriera uno de mis manías: nombrar implementaciones de interfaces.

Supongamos que tiene una interfaz Orderdiseñada para implementarse de varias maneras, pero solo existe la implementación inicial cuando se crea el proyecto por primera vez. ¿Vas por DefaultOrdero OrderImplalguna otra variante para evitar la falsa dicotomía? ¿Y qué haces cuando aparecen más implementaciones?

Y lo más importante ... ¿por qué?

Gary Rowe
fuente

Respuestas:

59

Los nombres tienen la oportunidad de transmitir significado. ¿Por qué desperdiciarías esa oportunidad con Impl?

En primer lugar, si solo tendrá una implementación, elimine la interfaz. Crea este problema de nombres y no agrega nada. Peor aún, podría causar problemas con firmas de métodos inconsistentes en las API si usted y todos los demás desarrolladores no tienen cuidado de usar solo la interfaz.

Dado eso, podemos suponer que cada interfaz tiene o puede tener dos o más implementaciones.

  • Si solo tiene uno en este momento y no sabe de qué manera el otro puede ser diferente, Default es un buen comienzo.

  • Si tiene dos en este momento, nombre cada uno de acuerdo a su propósito.

    Ejemplo: Recientemente, tuvimos un contexto de clase concreto (en referencia a una base de datos). Se dio cuenta de que necesitábamos poder representar un contexto que estaba fuera de línea, por lo que se utilizó el nombre Context para una nueva interfaz (para mantener la compatibilidad con las API antiguas), y se creó una nueva implementación, OfflineContext . ¿Pero adivinen el nombre del original? Así es, ContextImpl (yikes).

    En este caso, DefaultContext probablemente estaría bien y la gente lo obtendría, pero no es tan descriptivo como podría ser. Después de todo, si no está fuera de línea , ¿qué es? Entonces fuimos con: OnlineContext .


Caso especial: uso del prefijo "I" en las interfaces

Una de las otras respuestas sugirió usar el prefijo I en las interfaces. Preferiblemente, no necesitas hacer esto.

Sin embargo, si necesita una interfaz, para implementaciones personalizadas, pero también tiene una implementación concreta primaria que se usará con frecuencia, y el nombre básico para ella es demasiado simple como para renunciar a una interfaz sola, entonces puede considerar agregar "I" a la interfaz (sin embargo, está completamente bien si todavía no es adecuado para usted y su equipo).

Ejemplo: muchos objetos pueden ser un "EventDispatcher". Por el bien de las API, esto debe ajustarse a una interfaz. Pero también desea proporcionar un despachador de eventos básico para la delegación. DefaultEventDispatcher estaría bien, pero es un poco largo, y si va a ver el nombre a menudo, es posible que prefiera utilizar el nombre base EventDispatcher para la clase concreta e implementar IEventDispatcher para implementaciones 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
fuente
3
+1 para respuesta sólida. Como el razonamiento detrás de no usar Impl.
Gary Rowe
2
+1 absolutamente de acuerdo. Nombrar una interfaz lejos del concepto de dominio que representa está perdiendo completamente el punto.
Rupjones
99
"si solo tendrá una implementación", ¿cómo sabe de antemano que solo tendrá una implementación? "cada interfaz tiene o puede tener dos o más implementaciones" ... antes de tener dos implementaciones, primero no tiene ninguna, tiene una, luego tiene dos, quiero decir que puede haber un momento en el que solo tiene una implementación, solo antes de implementar el segundo.
Tulains Córdova
2
@NickC No estoy siendo pedante sobre semántica (soy poeta y ni siquiera lo sabía). El inglés no es mi lengua materna, así que no puedo ser pedante al respecto. Estaba hablando de lógica defectuosa. Utiliza interfaces para desacoplar. Eso no requiere un cierto número de implementaciones.
Tulains Córdova
44
if you will only ever have one implementation, do away with the interface- a menos que desee probar el componente, en cuyo caso es posible que desee mantener esa interfaz para crear MockOrder, OrderStub o similar.
JBRWilkinson
15

Decido el nombre por el caso de uso de la interfaz.

Si la interfaz se usa para desacoplar , elijo las Implimplementaciones.

Si el propósito de la interfaz es la abstracción del comportamiento , las implementaciones se nombran de acuerdo con lo que están haciendo concretamente. A menudo agrego el nombre de la interfaz a eso. Entonces, si se llama a la interfaz Validator, la uso FooValidator.

Encuentro que Defaultes una muy mala elección. Primero contamina las características de finalización del código, porque los nombres siempre comienzan con él. La otra cosa es que un valor predeterminado está sujeto a cambios con el tiempo. Entonces, lo que primero podría ser un valor predeterminado puede ser una característica obsoleta en algún momento. Entonces, siempre comienza a cambiar el nombre de sus clases tan pronto como cambian los valores predeterminados o vive con nombres engañosos.

SpaceTrucker
fuente
8

Estoy de acuerdo con la respuesta de Nicole (particularmente que la interfaz probablemente no sea necesaria en la mayoría de los casos), pero en aras de la discusión, descartaré una alternativa adicional que no sea OrderImply DefaultOrder: ocultar la implementación detrás de un método de fábrica estático como Orders.create(). Por ejemplo:

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

Con este enfoque, la implementación podría ser una clase interna anónima, o podría ser una clase privada con Defaulto Implen el nombre, o podría llamarse algo completamente diferente. Independientemente de la elección que haga, la persona que llama no necesita preocuparse, por lo que obtendrá más flexibilidad ahora y más adelante cuando / si decide cambiarla.

Algunos excelentes ejemplos de este patrón en la práctica son las clases de utilidad java.util.Collectionsy java.util.concurrent.Executors, cuyos métodos devuelven implementaciones ocultas. Como menciona Java Efectivo (en el Ítem 1), este patrón puede ayudar a mantener el "peso conceptual" de la API más pequeño.

Andrew McNamee
fuente
+1 para un caso adicional interesante: implementaciones anónimas.
Gary Rowe
1
También se usa ampliamente en la guayaba .
Nicole
3

Siempre voy OrderImplsimplemente porque aparece alfabéticamente justo después de la Orderinterfaz.

Ben Hoffstein
fuente
2
¿Y cómo maneja las implementaciones adicionales en curso, para un manejo especial, etc.?
Gary Rowe
1
Preguntaste sobre la implementación inicial. Una vez que comience a implementar DomesticOrder, ForeignOrder o lo que sea, se nombran con más cuidado y sin tener en cuenta su posición en el alfabeto.
Ben Hoffstein
Muy bien: he editado la pregunta original para reflejar este nuevo requisito.
Gary Rowe
No es una buena idea si habrá más de una implementación.
Sadegh
0

Puede nombrar la interfaz con el prefijo I (IWhatever) y luego realizar la implementación Cualquiera.

Las convenciones oficiales de código Java no informan sobre este tipo de nombres para interfaces, sin embargo, este tipo de nombres ayuda al reconocimiento y la navegación.

Matthieu
fuente
¿Por qué prefieres la interfaz con una I? ¿Es para ayudar al reconocimiento en la navegación?
Gary Rowe
@Gary: prefijar los tipos de interfaz con I es una convención bien establecida en el lenguaje Delphi. En Delphi, los tipos de clase tienen el prefijo T. Por lo tanto, tendríamos una interfaz IOrder y una implementación predeterminada de TOrder con TSomethingOrder y TBullMarketOrder como implementaciones específicas.
Marjan Venema
3
También he visto que esta convención se usa mucho en el desarrollo de .NET. Quizás porque la documentación y los ejemplos de Microsoft lo usan.
Ben Hoffstein
55
Parece que @Renesis ha presentado un caso útil para usarlo en Java. Personalmente, confiaría en el IDE para decirme la diferencia, de lo contrario tendríamos E's para Enums, C's para clases y todos en gran medida redundantes.
Gary Rowe
0

Creo que si bien Default puede tener sentido en algunos casos, sería más útil describir la implementación. Así que si su interfaz es UserProfileDAOentonces sus implementaciones pueden ser UserProfileSQLDAOo UserProfileLDAPDAOo algo por el estilo.

jiggy
fuente
0

si es posible, nómbrelo después de qué / cómo hace lo suyo.

por lo general, el peor enfoque es nombrarlo después de cómo se supone que debe usarse.

Si se supone que debe usarse como clase base para la implementación, puede usar BaseX o AbstractX (si es abstracto (pero intente apretar lo que hace, porque si no hiciera nada no lo crearía, ya tiene interfaz). Si proporciona la funcionalidad más simple posible y se espera que se use directamente (sin extenderla) cuando dicha funcionalidad sea suficiente, puede llamarla SimpleX o BasicX.

Si se usa a menos que se proporcione alguna otra implementación, asígnele el nombre DefaultX

user470365
fuente
-2

La mayoría de estas respuestas describen lo que se está haciendo pero no por qué.

¿Qué ha pasado con los principios OO? ¿Qué me dice Impl sobre una clase y cómo usarla? Debería preocuparse por comprender los usos, no cómo se construye.

Si creo una interfaz de persona, ¿qué es un PersonImpl? Sin sentido. Al menos DefaultPerson me dice que quien lo haya codificado no debería haber creado una interfaz.

Las interfaces están escribiendo para polimorfismo y deben usarse como tales.

usuario108620
fuente
1
La respuesta aceptada de @NickC entra en algunos detalles sobre lo que se está haciendo y por qué.
Gary Rowe