En este artículo hablamos del cuarto principio de los principios de programación SOLID.
Significado
Este principio establece que las clases clientes de otras no deberían verse forzados a depender de interfaces que no usan. En su lugar se apoya la definición de interfaces más específicas para cada caso.
Cuando hablamos de interfaces estamos hablando del concepto de interfaz de clase del lenguaje de programación Java así como la adaptación a cualquier otro tipo de lenguaje de programación. Por ejemplo en Swift estaríamos hablando de protocolos.
En pocas palabras una interface es un acuerdo entre las clases que implementan la interfaz y la propia interfaz definida con sus propiedades y funciones. El acuerdo consiste en que cada clase que implemente dicha interfaz deberá codificar cada una de las propiedades y funciones definidas en la interfaz.
Ejemplo
Este principio es más sencillo de entender con un ejemplo.
Imaginemos que estamos modelando una interfaz de acciones para distintas clases de aves.
interface Ave {
funcion comer()
funcion cantar()
funcion volar()
}
class Loro implements Ave {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion cantar() {
// código para cantar
}
funcion volar() {
// código para volar
}
}
class Aguila implements Ave {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion cantar() {
noHacerNada()
}
funcion volar() {
// código para volar
}
}
class Gallina implements Ave {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion cantar() {
// código para cantar
}
funcion volar() {
noHacerNada()
}
}
En este ejemplo tenemos una interfaz llamada Ave con 3 acciones pero no todas las aves realizan esas tres acciones. Por ejemplo las águilas no cantan y las gallinas no vuelan. Pero si una clase implementa una interfaz está obligada a incluir esas funciones aunque no hagan nada.
Solución
La solución consiste en segregar la interfaz Ave en 3 interfaces más específicas.
interface Ave {
funcion comer()
}
interface AveCantora {
funcion cantar()
}
interface AveVoladora {
funcion volar()
}
class Loro implements Ave, AveCantora, AveVoladora {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion cantar() {
// código para cantar
}
funcion volar() {
// código para volar
}
}
class Aguila implements Ave, AveVoladora {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion volar() {
// código para volar
}
}
class Gallina implements Ave, AveCantora {
propiedad nombre
propiedad color
propiedad tamaño
funcion comer() {
// código para comer
}
funcion cantar() {
// código para cantar
}
}
Ahora cada clase sólo implementa las interfaces necesarias a sus capacidades por lo que no hay funciones que no hacen nada.
Con esta solución es sencillo incluir en un futuro por ejemplo aves que puedan nadar y cada clase sólo implementará las funciones necesarias.
Además con esta solución se mejora aún más el principio de responsabilidad única.