Liskov Substitutio Principle [LSP]
Definición: Una clase debe poder usar cualquier clase derivada en lugar de una clase principal y hacer que se comporte de la misma maneda sin modificaciones. Asegura que una
clase derivada no afecte el comportamiento de la clase padre, o que una clase derivada debe ser sustituible por su clase padre.
Como de costumbre, las definiciones en sí no me dicen mucho, así que vamos a intentar explicar esto con un ejemplo del mundo real a ver si queda un poco más claro. Un padre es abogado mientras que su hijo quiere ser astronauta. Entonces aquí el hijo no puede reemplazar a su padre aunque ambos pertenezcan a la miama jerarquía familiar. Como puedes suponer, esto principio se puede decir que es un extensión del OCP y significa que debemos asegurarnos que las nuevas clases derivadas amplíen las clases base sin cambiar su comportamiento.
Pasemos a verlo con algo de código. Como ejemplo tenemos una clase base Perro que va a exponer un método GetRace que puede ser extendido desde otra clase. Y entonces vamos a
terner una clase Gato que extenderá de Perro para poder sobre escribir el método GetRace y devolver que tipo raza es. Como es lógico, un gato no debería de poder devolver una raza que sea de un perro.
Ejemplo que no cumple con LSP
public class Perro
{
public virtual string GetRace()
{
return "Chiguagua";
}
}
public class Gato : Perro
{
public override string GetRace()
{
return "Siames";
}
}
public class KnowRace
{
public void WhatRace()
{
Perro dog = new Perro();
Console.WriteLine(dog.GetRace());
//now i am a a cat
dog = new Gato();
Console.WriteLine(dog.GetRace());
}
}
Como se puede objervar en este ejemplo, se nos hace raro que para un objeto Perro lo podamos definir como un Gato y encima de devulva la raza de un gato. Es aquí donde viene Liskov al rescate, y como hemos dicho, haciendo algo muy parecido a lo que es OCP solucionamos este problema.
Ejemplo cumpliendo con LSP
public abstract class Race
{
public abstract string GetRace();
}
public class Perro : Race
{
public override string GetRace()
{
return "Chiguagua";
}
}
public class Gato : Race
{
public override string GetRace()
{
return "Siames";
}
}
public class KnowRace
{
public void WhatRace()
{
Race dog = new Perro();
Console.WriteLine(dog.GetRace());
//now i am a a cat
dog = new Gato();
Console.WriteLine(dog.GetRace());
}
}
Como se puede ver, en este caso ahora tengo una clase Race que es de la que van a heredar Perro y Gato para poder sobreescribir el método GetRace() y ahora es un poco más lógico y entendible el código, yo quiero una raza de un animal, ahora podríamos tener tambien Caballo o cualquier otros que extenda de Race y el código sería más entendible.
De hecho en lugar de clases abstractas lo podríamos haber hecho con un interfaz. La funcionalidad y la lógica sería la misma, lo he querido mostrar con clases abstractas, ya que estamos saturados de interfaces entre OCP y cuando expliquemos luego el siguiente principio de ISP.
Conclusiones
Como se puede observar este principio es muy simple y viene muy ligado del OCP por eso este blog no ha sido mu largo. Espero que se haya entendido. En mi caso es un principio que no suelo aplicar ya que no suelo hacer muchas clases derivadas y cuando lo uso mas bien es de forma indirecta, es decir, no me doy cuenta de que lo estoy aplicando, como creo que le pasará a muchos programadores, que sin saberlo implementan principios.