Comienza con un perro. En particular, un pug.
El pug tiene varios comportamientos:
public class Pug
{
private String name;
public Pug(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Arf!";
}
public boolean hasCurlyTail()
{
return true;
}
}
Y tienes un Labrador, que también tiene un conjunto de comportamientos.
public class Lab
{
private String name;
public Lab(String n)
{
name = n;
}
public String getName()
{
return name;
}
public String bark()
{
return "Woof!";
}
public boolean hasCurlyTail()
{
return false;
}
}
Podemos hacer algunos pugs y laboratorios:
Pug pug = new Pug("Spot");
Lab lab = new Lab("Fido");
Y podemos invocar sus comportamientos:
pug.bark() -> "Arf!"
lab.bark() -> "Woof!"
pug.hasCurlyTail() -> true
lab.hasCurlyTail() -> false
pug.getName() -> "Spot"
lab.getName() -> "Fido"
Digamos que dirijo una perrera y necesito hacer un seguimiento de todos los perros que albergo. Necesito almacenar mis pugs y labradores en matrices separadas:
public class Kennel
{
Pug[] pugs = new Pug[10];
Lab[] labs = new Lab[10];
public void addPug(Pug p)
{
...
}
public void addLab(Lab l)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
Pero esto claramente no es óptimo. Si también quiero alojar algunos caniches, debo cambiar mi definición Kennel
para agregar una matriz de Poodles
. De hecho, necesito una matriz separada para cada tipo de perro.
Insight: tanto los pug como los labradores (y los poodles) son tipos de perros y tienen el mismo conjunto de comportamientos. Es decir, podemos decir (a los efectos de este ejemplo) que todos los perros pueden ladrar, tener un nombre y pueden tener o no una cola rizada. Podemos usar una interfaz para definir lo que todos los perros pueden hacer , pero dejarlo en manos de los tipos específicos de perros para implementar esos comportamientos particulares. La interfaz dice "aquí están las cosas que todos los perros pueden hacer", pero no dice cómo se realiza cada comportamiento.
public interface Dog
{
public String bark();
public String getName();
public boolean hasCurlyTail();
}
Luego modifico ligeramente las clases Pug
y Lab
para implementar los comportamientos Dog
. Podemos decir que un Pug
es un Dog
y un Lab
es un Dog
.
public class Pug implements Dog
{
// the rest is the same as before
}
public class Lab implements Dog
{
// the rest is the same as before
}
Todavía puedo crear una instancia de Pug
sy Lab
s como lo hice anteriormente, pero ahora también tengo una nueva forma de hacerlo:
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
Esto dice que d1
no es solo un Dog
, es específicamente un Pug
. Y d2
también es un Dog
, específicamente un Lab
.
Podemos invocar los comportamientos y funcionan como antes:
d1.bark() -> "Arf!"
d2.bark() -> "Woof!"
d1.hasCurlyTail() -> true
d2.hasCurlyTail() -> false
d1.getName() -> "Spot"
d2.getName() -> "Fido"
Aquí es donde todo el trabajo extra se amortiza. La clase Kennel
se vuelve mucho más simple. Solo necesito una matriz y un método addDog
. Ambos trabajarán con cualquier objeto que sea un perro; es decir, los objetos que implementan la interfaz Dog
.
public class Kennel
{
Dog[] dogs = new Dog[20];
public void addDog(Dog d)
{
...
}
public void printDogs()
{
// Display names of all the dogs
}
}
He aquí cómo usarlo:
Kennel k = new Kennel();
Dog d1 = new Pug("Spot");
Dog d2 = new Lab("Fido");
k.addDog(d1);
k.addDog(d2);
k.printDogs();
La última declaración se mostraría:
Spot
Fido
Una interfaz le brinda la capacidad de especificar un conjunto de comportamientos que todas las clases que implementan la interfaz compartirán en común. En consecuencia, podemos definir variables y colecciones (como matrices) que no tienen que saber de antemano qué tipo de objeto específico mantendrán, solo que contendrán objetos que implementan la interfaz.