Metodología de interfaces Java: ¿Debería cada clase implementar una interfaz?

He estado progtwigndo en Java para algunos cursos en la Universidad y tengo la siguiente pregunta:

¿Está metodológicamente aceptado que cada clase debería implementar una interfaz? ¿Se considera una mala práctica no hacerlo? ¿Puedes describir una situación en la que no es una buena idea usar interfaces?

Editar: Personalmente, me gusta la idea de usar interfaces para todo como una metodología y hábito, incluso si no es claramente beneficioso. Eclipse creó automáticamente un archivo de clase con todos los métodos, por lo que no pierde tiempo de todos modos.

No necesita crear una interfaz si no la va a usar.

Por lo general, necesitas una interfaz cuando:

  • Su progtwig proporcionará varias implementaciones para su componente. Por ejemplo, una implementación predeterminada que es parte de su código, y una implementación simulada que se utiliza en una prueba JUnit. Algunas herramientas automatizan la creación de una implementación simulada, como por ejemplo EasyMock.
  • Desea utilizar la dependency injection para esta clase, con un marco como Spring o JBoss Micro-Container. En este caso, es una buena idea especificar las dependencias de una clase con otras clases utilizando una interfaz.

Siguiendo el principio de YAGNI , una clase debería implementar una interfaz si realmente la necesita. De lo contrario, ¿qué ganas con eso?

Edición: las interfaces proporcionan una especie de abstracción. Son particularmente útiles si desea intercambiar entre diferentes implementaciones (muchas clases que implementan la misma interfaz). Si es solo una clase, no hay ganancia.

Cada clase implementa una interfaz (es decir, un contrato) en la medida en que proporciona una API no privada. Si debe elegir representar la interfaz por separado como una interfaz Java depende de si la implementación es “un concepto que varía”.

Si está absolutamente seguro de que solo hay una implementación razonable, entonces no hay necesidad de una interfaz. De lo contrario, una interfaz le permitirá cambiar la implementación sin cambiar el código del cliente.

Algunas personas gritarán “YAGNI”, asumiendo que usted tiene control total sobre el cambio del código en caso de que descubra un nuevo requisito más adelante. Otras personas sentirán justamente temor de que tengan que cambiar lo inmutable, una API publicada.

Si no implementa una interfaz (y usa algún tipo de fábrica para la creación de objetos), ciertos tipos de cambios lo forzarán a romper el Principio de Abierto Cerrado. En algunas situaciones esto es comercialmente aceptable, en otros no lo es.

¿Puedes describir una situación en la que no es una buena idea usar interfaces?

En algunos idiomas (por ejemplo, C ++, C #, pero no en Java) puede obtener un beneficio de rendimiento si su clase no contiene métodos virtuales.

En progtwigs pequeños, o aplicaciones sin API publicadas, entonces puede ver un pequeño costo para mantener interfaces separadas.

Si observa un aumento significativo en la complejidad debido a la separación de la interfaz y la implementación, entonces probablemente no esté utilizando las interfaces como contratos. Las interfaces reducen la complejidad. Desde la perspectiva del consumidor, los componentes se convierten en productos que cumplen con los términos de un contrato en lugar de entidades que tienen detalles de implementación sofisticados por derecho propio.

No, no es necesario que cada clase implemente una interfaz. Use interfaces solo si hacen que su código sea más limpio y fácil de escribir.

Si su progtwig no necesita actualmente más de 1 implementación para una clase determinada, entonces no necesita una interfaz. Por ejemplo, en un simple progtwig de ajedrez que escribí, solo necesito 1 tipo de objeto de la Junta. Un tablero de ajedrez es un tablero de ajedrez es un tablero de ajedrez. Hacer una interfaz de la Junta e implementar eso habría requerido más código para escribir y mantener.

Es tan fácil cambiar a una interfaz si finalmente la necesita.

Crear una interfaz para cada clase es innecesario. Algunas de las razones comúnmente citadas incluyen el burlarse (innecesario con los marcos de burla modernos como Mockito) y para la dependency injection (por ejemplo, Spring, que también es innecesario en las implementaciones modernas).

Cree una interfaz si la necesita, especialmente para documentar formalmente las interfaces públicas. Hay un par de casos ingeniosos (por ejemplo, interfaces de marcadores).

Por lo que vale, en un proyecto reciente utilizamos interfaces para todo (tanto DI como burlas se mencionaron como razones) y resultó ser un desperdicio completo y agregó mucha complejidad: era tan fácil agregar una interfaz cuando realmente necesitaba burlarse de algo en los raros casos en que era necesario. Al final, estoy seguro de que alguien terminará entrando y eliminando todas las interfaces externas algún fin de semana.

Me doy cuenta de que los progtwigdores de C que se están mudando a Java tienden a gustar muchas interfaces (“son como encabezados”). La versión actual de Eclipse es compatible con esto, al permitir la navegación con control y clic para generar una ventana emergente que solicita una interfaz o implementación.

Descubrí que es beneficioso definir los métodos públicos de una clase en una interfaz correspondiente y cuando se definen referencias a otras clases, se usa estrictamente una referencia de interfaz. Esto permite una fácil inversión del control, y también facilita la prueba unitaria con burla y punteo. También le da la libertad de reemplazar la implementación con alguna otra clase que implemente esa interfaz, por lo que si está interesado en TDD puede hacer las cosas más fáciles (o más artificiales si es crítico con TDD).

Las interfaces son la forma de obtener un polymorphism. Entonces, si solo tiene una implementación, una clase de tipo particular, no necesita una interfaz.

Para responder a la pregunta del OP de una manera muy contundente: no, no todas las clases necesitan implementar una interfaz. Al igual que para todas las preguntas de diseño, esto se reduce al mejor juicio. Aquí hay algunas reglas generales que normalmente sigo:

  • Es probable que los objetos puramente funcionales no necesiten (por ejemplo, Pattern, CharMatcher, aunque este último implemente Predicate, es secundario a su función principal)
  • Los titulares de datos puros probablemente no necesiten (p. Ej. LogRecord, Locale)
  • Si puede visualizar una implementación diferente de una funcionalidad determinada (por ejemplo, memoria caché en memoria frente a memoria caché basada en disco), intente aislar la funcionalidad en una interfaz. Pero no vayas demasiado lejos tratando de predecir el futuro tampoco.
  • Para fines de prueba, es muy conveniente cuando las clases que hacen E / S o inician subprocesos son fácilmente modificables, de modo que los usuarios no paguen una penalización al ejecutar sus pruebas.
  • No hay nada peor que una interfaz que pierde su implementación subyacente. Presta atención donde trazas la línea y asegúrate de que el Javadoc de tu interfaz sea neutral de esa manera. Si no es así, probablemente no necesites una interfaz.
  • En términos generales, es preferible que las clases destinadas al consumo público fuera de su paquete / proyecto implementen interfaces para que sus usuarios estén menos acoplados a su implementación du jour.

Tenga en cuenta que probablemente pueda encontrar contraejemplos para cada una de las viñetas en esa lista. Las interfaces son muy potentes, por lo que deben usarse y crearse con cuidado, especialmente si proporciona API externas (vea este video para convencerse). Si eres demasiado rápido para poner una interfaz frente a todo, probablemente terminarás filtrando tu implementación única, y solo estás haciendo las cosas más complicadas para las personas que te siguen. Si no los usa lo suficiente, puede terminar con una base de código que es igualmente difícil de mantener porque todo está vinculado estáticamente y es muy difícil de cambiar. La lista no exhaustiva de arriba es donde trato de trazar la línea.

Una buena forma de aprender lo que se consideran buenas metodologías, especialmente cuando se trata del diseño de la estructura del código, es mirar el código disponible libremente. Con Java, el ejemplo obvio es echar un vistazo a las bibliotecas del sistema JDK .

Encontrará muchos ejemplos de clases que no implementan ninguna interfaz, o que están destinadas a ser utilizadas directamente, como java.util.StringTokenizer.

Si utiliza el patrón de Interfaz de Proveedor de Servicios en su aplicación, las interfaces son más difíciles de extender que las clases abstractas. Si agrega un método a la interfaz, todos los proveedores de servicio deben reescribirse. Pero si agrega un método no abstracto a la clase abstracta, ninguno de los proveedores de servicios debe reescribirse.

Las interfaces también dificultan la progtwigción si solo una pequeña parte de los métodos de interfaz usualmente tienen una implementación significativa.

Cuando diseño un nuevo sistema desde cero uso un enfoque orientado a componentes, cada componente (10 o más clases) proporciona una interfaz, esto me permite (a veces) reutilizarlos.

  • Al diseñar una herramienta (o un sistema simple), creo que esto no debe ser necesariamente un marco extensible. Introduzco interfaces cuando necesito una segunda implementación como opción.

  • Vi algunos productos que expusieron casi todas las funcionalidades de una interfaz, tomó demasiado tiempo para comprender la complejidad innecesaria.

Una interfaz es como un contrato entre un proveedor de servicios (servidor) y el usuario de dicho servicio (cliente).

  • Si estamos desarrollando un servicio web y estamos exponiendo las rutas restantes mediante clases de controlador, las clases de controlador pueden implementar interfaces y esas interfaces actúan como el acuerdo entre el servicio web y las otras aplicaciones que utilizan este servicio web.
  • Las interfaces Java como Serializable , Clonable y Remote se usan para indicar algo al comstackdor o JVM. Cuando JVM ve una clase que implementa estas interfaces, realiza algunas operaciones para admitir la serialización, la clonación o la invocación remota de métodos. Si su clase necesita estas características, tendrá que implementar estas interfaces.

El uso de la interfaz está a punto de hacer que su marco de aplicación sea resistente para cambiar. Como mencioné aquí ( Debates de Herencia Múltiple II: de acuerdo con Stroustrup ) la herencia múltiple se canceló en java y c #, lo cual lamento, siempre se debe usar Interface porque nunca se sabe cuál será el futuro.