El Liskov Substitution Principle o Principio de Sustitución de Liskov (introducido por Barbara Liskov en 1987) establece que "los objetos de una superclase deberían ser reemplazables con objetos de sus subclases sin afectar la correctitud del programa".
En términos más simples... si usamos una clase, en alguna parte del código, y esa clase la extendemos (heredamos), tenemos que poder reemplazarla con cualquiera de las clases hijas, y el programa debe seguir siendo válido.
# ¿Sustitución y Correctitud?
- Sustitución: Reemplazar instancias de una clase base con instancias de subclases.
- Correctitud: El programa sigue funcionando como se esperaba después de la sustitución.
El Liskov Substitution Principle asegura que las clases base sean completamente sustituibles por sus clases derivadas, lo que significa que las subclases no deben cambiar el comportamiento esperado de la base.
# ¿Por qué es importante?
Fiabilidad
Asegura que los componentes sean intercambiables sin efectos secundarios indeseados.
Reusabilidad
Facilita la reutilización de código al garantizar que las subclases actúen como se espera.
Mantenibilidad
El código es más fácil de mantener ya que las relaciones entre clases son claras y predecibles.
# Síntomas de que no se cumple
Podemos identificar cuándo no estamos respetando el Liskov Substitution Principle:
- Necesidad de verificar el tipo de una subclase antes de usar un método.
- Las subclases que lanzan excepciones en métodos que la clase base maneja.
- Los clientes de la clase base no pueden usar subclases sin conocer la diferencia.
Ejemplo
Imaginemos una aplicación que gestiona formas geométricas.
Sin Liskov Substitution Principle
public class Rectangle { protected int width; protected int height; public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } public int getArea() { return width * height; } } public class Square extends Rectangle { public void setWidth(int width) { this.width = this.height = width; } public void setHeight(int height) { this.width = this.height = height; } }
Si usamos un Square en
lugar de un Rectangle,
podemos causar comportamientos inesperados, cambiar la altura de un Square cambia
también su ancho.
Con Liskov Substitution Principle
Diseñamos las clases para asegurar la sustituibilidad mediante una abstracción común:
public abstract class Shape { public abstract int getArea(); } public class Rectangle extends Shape { private int width; private int height; // setters y getters public int getArea() { return width * height; } } public class Square extends Shape { private int side; public void setSide(int side) { this.side = side; } public int getArea() { return side * side; } }
Conclusiones
Al implementar el Liskov Substitution Principle
aseguramos que Square y
Rectangle puedan ser
usados de manera intercambiable donde se espera una Shape. Esto elimina la
posibilidad de efectos secundarios inesperados debido a suposiciones incorrectas sobre el
comportamiento de las subclases.
Resumen
El Principio de Sustitución de Liskov es esencial para un diseño de software robusto y mantenible. Fomenta una jerarquía de clases coherente y predecible, lo que a su vez facilita la extensibilidad y la reusabilidad del código.