resueltos - ¿Cuándo debería usar una interfaz en java?
public interface java (12)
Básicamente, puede elegir entre interfaces y clases abstractas cuando necesita "omitir" algunos detalles de implementación. La interfaz suele ser la mejor opción, ya que las clases de clientes pueden implementar cualquier cantidad de interfaces, pero solo pueden tener una superclase ("la herencia es un recurso escaso", como lo expresan).
¿Por qué querrías una clase abstracta o una interfaz? Porque a veces, cuando escribe un algoritmo, no le importa cómo se hace un subpaso específico, sino que lo hace de acuerdo con algún tipo de contrato. Un ejemplo sería la API Collections, donde List es una interfaz; por lo general, cuando utilizas una List
, no te importa si guarda cosas en una matriz, en una lista de nodos vinculados o en algún otro tipo de camino. Siempre que almacene las cosas que colocaste en el orden en que las colocaste allí, eres feliz.
Luego tenemos AbstractList
: una clase abstracta que implementa List
, que proporciona la implementación de casi todo lo que necesita una List
completa: para crear su propia implementación de List
, todo lo que tiene que hacer es extender AbstractList
y completar algunos métodos. Este es un excelente ejemplo de cuándo una clase abstracta es una buena opción: cuando desea proporcionar una implementación casi completa de algo, que solo carece de algunas lagunas que debe rellenar el código del cliente.
Sugerencia: si hace una clase abstracta que contiene solo métodos abstractos, probablemente deba hacer una interfaz de la misma.
Un buen ejemplo de cuándo utilizar exactamente las interfaces específicamente en Java sería ideal y cualquier decisión específica que se aplique.
De la page documentación de page
Considere usar interfaces si:
- Esperas que las clases no relacionadas implementen tu interfaz. Por ejemplo, muchos objetos no relacionados pueden implementar una interfaz Serializable.
- Desea especificar el comportamiento de un tipo de datos particular, pero no le preocupa quién implementa su comportamiento.
- Desea aprovechar la herencia múltiple de tipo.
Eche un vistazo a las preguntas relacionadas de SE con ejemplos de código que ya tienen buenas respuestas.
¿Hay más en una interfaz que tener los métodos correctos?
¿Cuál es la diferencia entre una interfaz y una clase abstracta?
¿Cómo debería haber explicado la diferencia entre una interfaz y una clase abstracta?
Eche un vistazo al tutorial de la colección JDK en el texto del enlace . Piensa en Colecciones. ¿Qué te viene a la mente? Se puede ordenar o no se puede pedir y puede tener duplicados o no.
So Collection es una interfaz con List (ordenado) y Set (desordenado) como subinterfaces. Ahora hay muchas preguntas con Listas, debería estar sincronizada o no, debería ser una lista enlazada o no, etc. Cada "comportamiento" tendrá sus propias interfaces / clases abstractas.
Las clases abstractas son necesarias cuando desea especificar "algún" comportamiento en las colecciones. Por ejemplo, todas las colecciones (conjuntos / listas, etc.) pueden tener una representación "toString" que simplemente itera sobre los elementos (ordenados o no) y los vuelven a codificar. Ese comportamiento puede estar presente en "AbstractCollection", etc.
Si sigue la jerarquía de las colecciones JDK, es un excelente lugar para aprender sobre interfaces y clases abstractas :)
Las interfaces se usan cuando necesita múltiples implementaciones del mismo comportamiento. Aquí hay un ejemplo de una interfaz que los objetos podrían implementar para mostrar que todos pueden ser serializados a XML.
public interface Xmlizable
{
public String toXML();
}
luego puede pasar las interfaces "Xmlizable" a métodos que solo se preocupan por esa interfaz.
Las interfaces son necesarias donde se espera volatilidad en su programa, puntos en los que anticipa cambios, puntos donde su diseño necesita doblarse.
La implementación es frágil en este sentido: se rompe con bastante facilidad. Esta es la razón por la que la creación de subclases no siempre es la mejor solución, al igual que los métodos largos que implementan un comportamiento complicado por sí solos generalmente son una mala idea.
Las interfaces son más flexibles y pueden manejar mucho más estrés en el diseño de su programa que la implementación.
Al introducir interfaces en su programa, realmente introduce puntos de variación en los que puede conectar diferentes implementaciones para esa interfaz. El propósito principal de las interfaces es la abstracción , desacoplando el "qué" del "cómo".
Una regla importante a tener en cuenta para hacerlo de manera segura es el Principio de Sustitución de Liskov [ UncleBob , Wikipedia ]. Mientras que un compilador en un lenguaje como Java se asegurará de que sintácticamente todo esté en orden (número correcto de parámetros, tipos, ...), LSP se ocupa de la semántica . En resumen, LSP dice que cada implementación de una interfaz debe (también) comportarse correctamente para ser realmente sustituible como se describe anteriormente.
Trata de entender el patrón de diseño de estrategia
Un buen lugar para mirar sería el marco de colecciones.
java.util.List //interface
java.util.ArrayList //Concrete class
java.util.LinkedList //Concrete class
Entonces puedes escribir código como este:
List l = new ArrayList();
l.add(..)
//do something else.
Si en el futuro desea cambiar la implementación con, por ejemplo, LinkedList
o posee AwesomeList which implements List
interfaz de AwesomeList which implements List
, todo lo que tiene que hacer es cambiar la primera línea a:
List l = new MyAwesomeList();
or
List l = new LinkedList();
El resto del código seguiría adelante.
Un principio básico de OOP es ocultar información: ocultar los detalles de implementación y mostrar solo una descripción de los servicios básicos a la persona que llama.
Java tiene que construir para este objetivo: interfaces y clases abstractas. Puede definir una interfaz y escribir su código, que solo depende de la interfaz llamando a los "métodos disponibles" definidos en ella.
El ocultamiento de información se puede usar tanto para leer clases externas (externas en el sentido de que provienen del módulo que está escribiendo): de esta manera puede definir qué métodos necesita, y no es necesario inferir su tipo de implementación concreta, o cuándo definir un tipo de datos utilizable fuera de las clases; un ejemplo típico para esto es, por ejemplo, la API de recopilación o J2EE, como ya se ha mencionado.
Tanto las interfaces como las clases abstractas proporcionan estos detalles, pero existen dos diferencias principales: las interfaces admiten herencia múltiple, mientras que las clases abstractas pueden contener una implementación básica. Para maximizar su eficiencia, al definir una interfaz, también debe definir una clase abstracta con una implementación predeterminada significativa. Si el usuario de la interfaz no necesita extender ninguna otra clase, puede extender esta clase abstracta, de lo contrario, debe implementar todos los métodos desde la interfaz. Por otro lado, asegúrese de no leer esta clase abstracta directamente, la interfaz debería ser suficiente como abstracción.
Use interfaces siempre que planee reemplazar una clase de implementación por otra clase en algún punto de desarrollo.
También recomendaría usar backends de interfaz para todas las clases en relaciones de herencia, al menos en proyectos más serios: Desafortunadamente, ya no tengo el enlace, pero un desarrollador del lenguaje Java dijo una vez, que incluir la herencia de clase fue el mayor error en diseñando el lenguaje.
Los argumentos son bastante buenos: si se utiliza el diseño adecuado, siempre es posible reemplazar la herencia de clase por la herencia de la interfaz y se gana mucho con respecto al mantenimiento del código. También es más fácil preservar las relaciones de tipo naturales (por ejemplo, desde la geometría ("un cuadrado es un rectángulo") con la herencia de clase.
Utilice interfaces para definir un contrato de programación de aplicaciones (blueprint, interfaz) que los proveedores de "terceros" deben cumplir e implementar por completo. De esta forma, los usuarios finales pueden codificar contra el contrato API y cambiar fácilmente la implementación concreta "debajo de las capotas" sin cambiar el código.
La API JDBC es un excelente ejemplo. Existe casi solo interfaces. Las implementaciones concretas se proporcionan como "controladores JDBC". Esto le permite escribir todo el código JDBC independiente del proveedor de la base de datos (DB). Puede cambiar el controlador JDBC sin cambiar ninguna línea de código Java (excepto de cualquier código SQL específico de BD codificado) siempre que desee cambiar de proveedor de DB.
Otro ejemplo es la API de Java EE , también contiene casi interfaces y clases abstractas. Las implementaciones concretas se proporcionan como "servidores de aplicaciones Java EE", "Servletcontainers", etc., como Sun Glassfish, Apache Tomcat, etc. Esto le permite implementar la aplicación web (WAR) en cualquier servidor web Java que desee.
Esta respuesta es esencialmente la misma que la de coolest_head, solo un poco más explícita para transmitir la utilidad.
Como explica coolest_head, las interfaces son útiles cuando es posible que desee cambiar los posibles subcomponentes de su programa en el futuro. También le permiten separar preocupaciones de varias partes de la estructura de su programa más fácilmente ya que con las interfaces puede asegurarse de que ciertas clases no relacionadas, etc. simplemente no sean visibles para otras partes del programa.
Como ejemplo, supongamos que desea leer datos arbitrarios e imprimirlos, de esta manera:
SomeReader someReader = new SomeReader();
String data = someReader.readLine();
System.out.println(data);
Nada elegante aquí, ¿verdad? Pero, aunque este ejemplo es simple, ya está vinculado a la clase SomeReader
, lo que significa que todos los cambios que realice en esa clase se deben propagar a la clase en la que está utilizando la clase, ¡especialmente si refactoriza algunas partes internas! En cambio, quieres hacer esto
IMyReader reader = new SomeReader();
System.out.println(reader.readLine());
Ya casi está allí: ahora el código de impresión ya no se preocupa por la implementación específica, solo acerca de las partes expuestas por la interfaz. Esto suele ser suficiente porque ahora puede cambiar esa new
instrucción y obtener nuevas implementaciones y lo que aún funciona como se espera (¡ siempre que se respete el contrato de la interfaz entre las clases de implementación! ). Esto es especialmente útil cuando terminas usando ese objeto específico varias veces, aquí lo estoy usando solo una vez, pero realmente, si trabajas con listas de ejemplo, ¿cuántas operaciones usualmente haces la misma lista?
Por lo tanto, para realmente explotar este ejemplo de proporciones, esto es lo que su código puede terminar luciendo como
public class RowPrinter {
private final IMyReader reader;
public RowPrinter(IMyReader reader) {
this.reader = reader;
}
public void print() {
IMyReader reader = getReader();
System.out.println(reader.readLine());
}
protected IMyReader getReader() {
return reader;
}
}
¿Note esa parte con el constructor? Esa es la inversión de control y déjenme decirles, esa es una genial pieza de ingeniería de software allí mismo. Puedo hablar desde la experiencia que te ayuda con muchas molestias, ya sea cambiando de producto de base de datos a otro o haciendo que ciertas partes del hilo de código sean seguras. O tal vez solo quiera agregar una capa de registro a alguna clase, fácilmente factible con un decorator envoltura que implemente la misma interfaz que la clase envolvente. Y esto es sólo el principio.
Las interfaces traen muchos beneficios que generalmente no son tan obvios a partir de ejemplos simples, aunque los ejemplos simples lo hacen funcionar correctamente. Si bien las interfaces en Java son una construcción de lenguaje, en realidad son más un paradigma de programación que solo una característica de un solo idioma, en algunos lenguajes la emulación de interfaces realmente es beneficiosa si se descubre la forma correcta de hacerlo.
Interfaces en comunicación remota:
Las interfaces también se pueden usar para definir un "protocolo" acordado para comunicarse entre diferentes partes del sistema, posiblemente a través de llamadas remotas. Por lo tanto, la interfaz solo define qué se puede llamar, con qué parámetros y qué se devolverá después de la llamada. El cliente usa la interfaz y el servidor implementa el código real concreto, por ejemplo.
Nota al costado:
En Java solo puede ser inherente (extender) desde una clase, pero puede implementar más de una interfaz, por lo que a veces tendrá que usar interfaces cuando necesite ese tipo de herencia múltiple y cuando decida no usar composición sobre herencia.