Codigo limpio y refactorización

La inyección de dependencias es un principio de diseño que consiste en proporcionar las dependencias que una clase necesita desde fuera de ella, en lugar de que la clase las cree o gestione directamente. Esto mejora el bajo acoplamiento y facilita la prueba y la reutilización del código.


Ejemplo Malo: Sin Inyección de Dependencias

1class Arma {
2    public void golpear() {
3        System.out.println("Golpe con el arma!");
4    }
5}
6
7class Jugador {
8    private Arma arma;
9
10    public Jugador(Arma arma) {
11        this.arma = arma; // El jugador recibe el arma a través del constructor (inyección de dependencias)
12    }
13
14    public void golpear() {
15        arma.golpear();
16    }
17}
18
19public class Main {
20    public static void main(String[] args) {
21        Arma arma = new Arma();
22        Jugador jugador = new Jugador(arma);  // Se inyecta el arma al jugador
23        jugador.golpear();  // "Golpe con el arma!"
24    }
25}

Ejemplo Corregido: Con Inyección de Dependencias

Explicación de la mejora:

  1. Desacoplamiento: Antes, el jugador creaba su propia arma dentro de la clase, lo que generaba un fuerte acoplamiento entre el jugador y el tipo específico de arma. Al inyectar la arma desde fuera (a través del constructor), el jugador ya no necesita saber qué tipo de arma está usando, solo sabe que tiene una que implementa la interfaz Arma.

  2. Flexibilidad: Esto permite que el jugador pueda usar cualquier tipo de arma que implemente la interfaz Arma (por ejemplo, una espada, martillo, o incluso una nueva arma que creemos en el futuro) sin necesidad de modificar la clase Jugador.

  3. Testabilidad: Inyectar dependencias también facilita las pruebas unitarias, ya que puedes reemplazar el objeto Arma por un objeto simulado o mock, sin necesidad de alterar la clase Jugador.

En resumen, la mejora principal es hacer el código más flexible y reutilizable al permitir que las dependencias (en este caso, el tipo de arma) sean proporcionadas desde fuera en lugar de ser creadas dentro de la clase.

1// Definimos la interfaz Arma
2interface Arma {
3    void golpear();
4}
5
6// Implementación de Arma con Espada
7class Espada implements Arma {
8    public void golpear() {
9        System.out.println("Golpe con la espada!");
10    }
11}
12
13// Implementación de Arma con Martillo
14class Martillo implements Arma {
15    public void golpear() {
16        System.out.println("Golpe con el martillo!");
17    }
18}
19
20// Clase Jugador, ahora con inyección de dependencias
21class Jugador {
22    private Arma arma;
23
24    public Jugador(Arma arma) {
25        this.arma = arma;  // Inyección de dependencias a través del constructor
26    }
27
28    public void golpear() {
29        arma.golpear();
30    }
31}
32
33public class Main {
34    public static void main(String[] args) {
35        // Crear un jugador con una espada
36        Arma espada = new Espada();
37        Jugador jugador1 = new Jugador(espada);
38        jugador1.golpear();  // "Golpe con la espada!"
39
40        // Crear un jugador con un martillo
41        Arma martillo = new Martillo();
42        Jugador jugador2 = new Jugador(martillo);
43        jugador2.golpear();  // "Golpe con el martillo!"
44    }
45}

Otro ejemplo

Ejemplo Malo: Sin Inyección de Dependencias

1public class OrderService {
2    private CreditCardProcessor paymentProcessor = new CreditCardProcessor();
3
4    public void processOrder(String productName) {
5        System.out.println("Processing order for: " + productName);
6        paymentProcessor.processPayment();
7    }
8}
9
10class CreditCardProcessor {
11    public void processPayment() {
12        System.out.println("Processing credit card payment...");
13    }
14}

Problemas en los Ejemplos Malos

  1. Alto Acoplamiento: OrderService está directamente acoplado a CreditCardProcessor, dificultando cambios o sustituciones por otra implementación.
  2. Dificultad en Pruebas: No es fácil probar OrderService de manera aislada porque no se puede sustituir CreditCardProcessor por un mock.
  3. Extensibilidad Limitada: Agregar un nuevo método de pago requiere modificar la lógica de OrderService.
  4. Reutilización: OrderService no puede trabajar con cualquier implementación de PaymentProcessor, haciéndolo no reutilizable.

Ejemplo Corregido: Con Inyección de Dependencias

1public class OrderService {
2    private PaymentProcessor paymentProcessor;
3
4    public OrderService(PaymentProcessor paymentProcessor) {
5        this.paymentProcessor = paymentProcessor;
6    }
7
8    public void processOrder(String productName) {
9        System.out.println("Processing order for: " + productName);
10        paymentProcessor.processPayment();
11    }
12}
13
14interface PaymentProcessor {
15    void processPayment();
16}
17
18class CreditCardProcessor implements PaymentProcessor {
19    public void processPayment() {
20        System.out.println("Processing credit card payment...");
21    }
22}
23//Uso
24PaymentProcessor paymentProcessor = new CreditCardProcessor();
25OrderService orderService = new OrderService(paymentProcessor);
26orderService.processOrder("Laptop");
  • Loading...