2008-11-01 10 views
19

Leí por todas partes cómo Spring le anima a usar interfaces en su código. Yo no lo veo No hay ninguna noción de interfaz en su configuración Spring Xml. ¿Qué parte de Spring realmente lo anima a usar interfaces (que no sean los documentos)?muelles e interfaces

+1

Además de los puntos planteados sobre DI, cosas como Spring Remoting son totalmente dependientes del uso de interfaces. – Robin

+0

Esa es una muy buena pregunta. Y como puede ver en las respuestas hasta ahora, nadie ha dado una respuesta específica. –

Respuesta

25

Cuando se define una interfaz para sus clases, que ayuda con la inyección de dependencias. Los archivos de configuración de Spring no tienen nada sobre las interfaces en sí mismos; solo debe poner el nombre de la clase.

Pero si quiere inyectar otra clase que ofrezca una funcionalidad "equivalente", usar una interfaz realmente ayuda.

Por ejemplo, diciendo que tienes una clase que analiza el contenido de un sitio web y lo estás inyectando con Spring. Si las clases en las que lo está inyectando saben cuál es la clase real, entonces para cambiarla tendrá que cambiar una gran cantidad de código para usar una clase concreta diferente. Pero si creó una interfaz Analyzer, podría fácilmente inyectar su DefaultAnalyzer original como podría burlarse de DummyAnalyzer o incluso de otra que haga esencialmente lo mismo, como PageByPageAnalyzer o cualquier otra cosa. Para usar uno de ellos, solo tiene que cambiar el nombre de clase que está inyectando en sus archivos de configuración de Spring, en lugar de ir a través de su código cambiando las clases.

Me tomó alrededor de un proyecto y medio antes de que realmente comenzara a ver la utilidad. Como la mayoría de las cosas (en los lenguajes de empresa) que terminan siendo útiles, al principio parece una adición inútil de trabajo, hasta que su proyecto comienza a crecer y luego descubre cuánto tiempo ahorró haciendo un poco más de trabajo por adelantado.

+6

Si bien lo que dices es correcto, no está centrado en la primavera. Está centrado en Java. –

2

Probablemente quiera intentar usarlo usted mismo para poder ver mejor esto, puede no quedar claro en los documentos cómo Spring alienta el uso de la interfaz.

Aquí hay un par de ejemplos:

  1. Digamos que usted está escribiendo una clase que necesita para leer de un recurso (por ejemplo, archivo) que pueden hacer referencia en varias formas (por ejemplo, en la ruta de clase, ruta absoluta del archivo, como una URL, etc.). Desearía definir una propiedad org.springframework.core.io.Resource (interfaz) en su clase. Luego, en su archivo de configuración de Spring, simplemente seleccione la clase de implementación real (por ejemplo, org.springframework.core.io.ClassPathResource, org.springframework.core.io.FileSystemResource, org.springframework.core.io.UrlResource, etc.). Spring funciona básicamente como una fábrica extremadamente genérica.

  2. Si desea aprovechar la integración de AOP de Spring (para agregar interceptores de transacción, por ejemplo), necesitará prácticamente definir interfaces. Usted define los puntos de interceptación en su archivo de configuración de Spring, y Spring genera un proxy para usted, basado en su interfaz.

Estos son ejemplos con los que personalmente tengo experiencia. Estoy seguro de que hay mucho más por ahí.

31

El Dependency Inversion Principle lo explica muy bien. En particular, figura 4.

A. Los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deberían depender de las abstracciones.

B. La abstracción no debe depender de los detalles. Los detalles deben depender de las abstracciones.

La traducción de los ejemplos de funciones del foro en java:

public class Copy { 
    private Keyboard keyboard = new Keyboard(); // concrete dependency 
    private Printer printer = new Printer(); // concrete dependency 
    public void copy() { 
     for (int c = keyboard.read(); c != KeyBoard.EOF) { 
      printer.print(c); 
     } 
    } 
} 

Ahora, con la inversión de dependencia:

public class Copy { 
    private Reader reader; // any dependency satisfying the reader interface will work 
    private Writer writer; // any dependency satisfying the writer interface will work 
    public void copy() { 
     for (int c = reader.read(); c != Reader.EOF) { 
      writer.write(c); 
     } 
    } 
    public Copy(Reader reader, Writer writer) { 
     this.reader = reader; 
     this.writer = writer; 
    } 
} 

Ahora Copy apoya más que la copia desde un teclado a una impresora.

Es capaz de copiar desde cualquier Reader a cualquier Writer sin requerir ninguna modificación de su código.

Y ahora con la primavera:

<bean id="copy" class="Copy"> 
    <constructor-arg ref="reader" /> 
    <constructor-arg ref="writer" /> 
</bean> 

<bean id="reader" class="KeyboardReader" /> 
<bean id="writer" class="PrinterWriter" /> 

o tal vez:

<bean id="reader" class="RemoteDeviceReader" /> 
<bean id="writer" class="DatabaseWriter" /> 
9

La mayoría de las respuestas aquí son alguna forma de "Usted puede cambiar implementaciones fácilmente", pero lo que creo que no pueden responder es por qué? parte. A eso creo que la respuesta es casi definitivamente comprobable. Independientemente de si usa Spring o cualquier otro marco de IOC, el uso de Dependency Injection hace que su código sea más fácil de probar. En el caso de decir un escritor en lugar de un PrinterWriter, puede simular la interfaz Writer en una prueba unitaria y asegurarse de que su código lo llame de la manera que esperaba. Si depende directamente de la implementación de la clase, su única opción es dirigirse a la impresora y verificarla, lo cual no es muy automático. Además, si depende del resultado de una llamada a una clase, no poder simular puede evitar que pueda alcanzar todas las rutas de código en su prueba, reduciendo así su calidad (potencialmente) En pocas palabras, debe desacoplar el objeto creación de gráfico desde la lógica de la aplicación. Hacerlo hace que tu código sea más fácil de probar.

+0

No he visto ninguna evidencia que respalde la afirmación de que las pruebas unitarias mejoran la calidad/confiabilidad/velocidad del software. – Chad

+5

@Chad ¿Estás haciendo trolling? – Ryan

0

Spring no lo forzará a usar interfaces en ninguna parte, es solo una buena práctica. Si tiene un bean que tiene algunas propiedades que son interfaces en lugar de clases concretas, puede simplemente cambiar algunos objetos con maquetas que implementan la misma interfaz, lo cual es útil para ciertos casos de prueba.

Si utiliza, por ejemplo, las clases de soporte de Hibernate, puede definir una interfaz para su DAO, y luego implementarla por separado; la ventaja de tener la interfaz es que podrá configurarla utilizando los interceptores Spring, lo que le permitirá simplificar su código; no tendrá que escribir ningún código en HibernateExceptions y cerrar la sesión en un segmento finally, y tampoco tendrá que definir ninguna transacción de forma programática, simplemente configure todo eso de manera declarativa con Spring.

Cuando está escribiendo aplicaciones rápidas y sucias, puede implementar algunas DAO simples usando JDBC o algún framework simple que no terminará usando en la versión final; podrá cambiar fácilmente esos componentes si implementan algunas interfaces comunes.

1

es fácil generar proxies desde las interfaces.

si observas cualquier aplicación de primavera, verás las interfaces de servicio y persistencia. haciendo que la expresión primaveral fomente el uso de interfaces. no se vuelve más explícito que eso.

1

Nadie ha mencionado todavía que en muchas ocasiones no será necesario crear una interfaz para que la clase implementadora se pueda cambiar rápidamente porque simplemente no habrá más de una clase implementadora.

Cuando las interfaces se crean sin necesidad, las clases se crearán por pares (interfaz más implementación), agregando interfaces repetitivas innecesarias y creando posibles confusiones de dependencia porque, en los archivos de configuración XML, los componentes se referenciarán a veces por su interfaz y algunas veces por su implementación, sin consecuencias en el tiempo de ejecución pero siendo incoherente con respecto a las convenciones de códigos.

1

Si no utiliza interfaces, se arriesga a una falla de autowiring: Sometime Spring crea una clase Proxy para un Bean. Esta clase Proxy no es una clase hija de la implementación del servicio, pero implementa de nuevo todas sus interfaces. Spring intentará autoaumentar instancias de este Bean, sin embargo esta clase Proxy es incompatible con la clase Bean. Por lo tanto, declarar un campo con la clase Bean puede generar excepciones de "asignación insegura de campos".

No se puede saber con certeza cuándo Spring está yendo al servicio Proxy a (no debería hacerlo), así que para protegerse de esas sorpresas, lo mejor es declarar una interfaz y usar esta interfaz al declarar campos autocableados.

Cuestiones relacionadas