El artículo de hoy va ser un poco técnico y enfocado a Java EE 7. Vamos a hablar sobre la inyección de dependencias. Imagino que a estas alturas no habrá un solo desarrollador Java/Java EE que no sepa lo que es esto. Para aquellos despistados añadiré, que la inyección de dependencias es un patrón en el que se le suministran objetos a una clase, en lugar de que la clase los cree. Para el que aún no sepa lo que es, le recomiendo que lo mire antes de proseguir leyendo porque el objetivo del artículo es solo dejar unos breves apuntes sobre la parte técnica, y no el de dar una amplia explicación sobre este concepto.
Para empezar, vamos a ver un ejemplo de como crearíamos un dependencia sin utilizar inyección. Esto sería utilizando la palabra reservado “new” como se ha hecho durante mucho tiempo. Para los ejemplos, vamos a utilizar una especie de sistema bancario de tarjetas, el cual adaptaremos a nuestras necesidades para la explicación.
Bien, pues aquí tenemos el primer ejemplo de dependencias sin inyección:
public class CustomerService { private Card card; public CustomerService() { this.card = new DebitCard(); } public Customer createCustomer(String name, String surname) { Customer customer = new Customer(name, surname); customer.setCardNumber(card.generateNumber()); return customer; } }
Esto sería el modo tradicional de la creación de un objeto y el manejo de este. En caso de tener varios tipos de tarjetas, podríamos utilizar el constructor para crear un tipo u otro:
public class CustomerService { private Card card; public CustomerService(Card card) { this.card = card; } public Customer createCustomer(String name, String surname) { Customer customer = new Customer(name, surname); customer.setCardNumber(card.generateNumber()); return customer; } } CustomerService customerService = new CustomerService(new DebitCard()); CustomerService customerService = new CustomerService(new CreditCard());
Con esto vemos como podemos pasar el tipo de tarjeta a nuestro servicio según cual necesitemos en cada momento.
Ahora bien, para ahorrarnos esto, la injección de dependencias viene en nuestra ayuda pudiendo hacer cosas como la que vemos a continuación y donde será nuestro contenedor el que maneje el objeto y se encargue de hacer el “new”:
public class CustomerService { @Inject private Card card; public Customer createCustomer(String name, String surname) { Customer customer = new Customer(name, surname); customer.setCardNumber(card.generateNumber()); return customer; } } public DebitCard implements Card { public String generateNumber() { ... } }
Existen varias formas de hacer la inyección del objeto (varios puntos de inyección): en la propiedad, en el constructor o en el método “set”. Técnicamente no hay una mejor o una peor, esto queda un poco a gusto del desarrollador. Los ejemplo de cada una de ellas serían los siguientes:
@Inject private Card card;
@Inject public CustomerService(Card card) { this.card = card; }
@Inject public void setCard(Card card) { this.card = card; }
Ahora bien, más de uno se estará preguntando en este punto que sucedería en caso de haber más de una clase que implementara el interfaz “Card”. Pues bien, aquí es donde los “Qualifiers” cobran su importancia, permitiéndonos establecer que clase es la que debe ser inyectada. Como ejemplo, vamos a crear los “Qualifiers” de nuestras tarjetas de crédito y débito:
@Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface Debit { }
@Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface Credit { }
Que usaríamos en las clases correspondientes:
@Debit public DebitCard implements Card { public String generateNumber() { ... } } @Credit public CreditCard implements Card { public String generateNumber() { ... } }
Y a la hora de la inyección el código quedaría tal que así:
public class CustomerDebitService { @Inject @Debit private Card card; public Customer createCustomer(String name, String surname) { Customer customer = new Customer(name, surname); customer.setCardNumber(card.generateNumber()); return customer; } } public class CustomerCreditService { @Inject @Credit private Card card; public Customer createCustomer(String name, String surname) { Customer customer = new Customer(name, surname); customer.setCardNumber(card.generateNumber()); return customer; } }
Pero claro, esto con solo dos clases está muy bien y es manejable, pero imaginemos ahora que sacamos más modelos de tarjetas: normal, online, endMonthPay y premium. Y además, todas ellas pueden ser de débito o de crédito. Está claro que se volvería inmanejable además de complicar innecesariamente con clases extras nuestra aplicación. Para evitar esto, en los “Qualifiers” podemos definir miembros, o lo que es lo mismo, propiedades para el “Qualifier”. Así que vamos a ver el ejemplo de como sería esto:
@Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface CardType { CType value(); boolean debit(); } public enum CType { NORMAL, ONLINE, END_MONTH_PAY, PREMIUM }
En este caso el código quedaría así para una tarjeta de débito normal:
@CardType(value = CType.NORMAL, debit = true) public DebitCard implements Card { public String generateNumber() { ... } } @Inject @CardType(value = CType.NORMAL, debit = true) private Card card;
Bueno, hasta aquí el pequeño esquema de hoy sobre inyección de dependencias y “Qualifiers”. Para cualquier duda, ya sabéis. Nos vemos.