====== Principio de Inversión de Dependencias ======
Este principio recomienda que una clase cliente establezca dependencias prioritariamente con abstracciones y no con implementaciones concretas, ya que las abstracciones (es decir, interfaces) son más estables que las implementaciones concretas (es decir, clases). La idea es, entonces, intercambiar (o invertir) las dependencias: en lugar de depender de clases concretas, los clientes deben depender de interfaces. Por lo tanto, un nombre más intuitivo para el principio sería *Prefiera Interfaces a Clases*.
Para detallar la idea del principio, supongamos que existe una interfaz ''I'' y una clase ''C1'' que la implementa. Si puede elegir, un cliente debe acoplarse a ''I'' y no a ''C1''. El motivo es que cuando un cliente se acopla a una interfaz ''I'', queda inmune a los cambios en la implementación de esa interfaz. Por ejemplo, en lugar de ''C1'', se puede cambiar la implementación a ''C2'', y esto no tendrá impacto en el cliente en cuestión.
**Ejemplo 1**: El siguiente código ilustra el escenario que acabamos de describir. En este código, el mismo Cliente puede trabajar con objetos concretos de las clases ''C1'' y ''C2''. No necesita conocer la clase concreta que está detrás, o que implementa, la interfaz ''I'' a la que referencia en su código.
interface I { ... }
class C1 implements I {
...
}
class C2 implements I {
...
}
class Cliente {
I i;
Cliente (I i) {
this.i = i;
...
} ...
}
class Main {
void main () {
C1 c1 = new C1();
new Cliente(c1);
...
C2 c2 = new C2();
new Cliente(c2);
...
}
}
**Ejemplo 2**: Ahora, mostramos un ejemplo de código que sigue el Principio de Inversión de Dependencias. Este principio justifica la elección de ''Proyector'' como tipo del parámetro del método ''g''. Mañana, el tipo de la variable local ''proyector'' en el método ''f'' podría cambiar a, por ejemplo, ''ProyectorSamsung''. Si eso llegara a suceder, la implementación de ''g'' seguiría siendo válida, ya que al usar un tipo interfaz nos estamos preparando para recibir parámetros de varios tipos concretos que implementen esa interfaz.
void f() {
...
Proyector proyector = new ProyectorLG();
...
g(proyector);
}
void g(Proyector proyector) {
...
}
**Ejemplo 3**: Como ejemplo final, supongamos un paquete de estructuras de datos que ofrece una interfaz ''List'' y algunas implementaciones concretas (clases) para ella, como ''ArrayList'', ''LinkedList'' y ''Vector''. Siempre que sea posible, en el código cliente de ese paquete, declara variables, parámetros o atributos usando el tipo `List`, ya que de esta manera estarás creando código compatible con las diversas implementaciones concretas de esa interfaz.