
Creo que este debe ser el principio SOLID más complejo de interiorizar. En concreto este principio dice que:
Si por cada objeto O1 de tipo S hay un objeto O2 de tipo T tal que para todos los programas P definidos en términos de T, el comportamiento de P no se ve alterado cuando O1 es sustituido por O2 entonces S es un subtipo de T.
Barbara Liskov – 1988
Existe otra definición un poco menos tortuosa de entender que dice
si S es un subtipo de T, entonces los objetos de tipo T pueden ser sustituidos por objetos de tipo S
Wikipedia.org – 2023
!Weón!, que chucha este concepto; se preguntarán. Pero analicémoslo en consecuencia para aclarar dudas. Lo primero que hay que entender, es que acá, particularmente acá, hablamos de herencia.
- Nos dice, S es un subtipo de T. o sea:

2. Luego, nos dice que todos los objetos de tipo T, pueden eventualmente ser reemplazados por S sin alterar el correcto funcionamiento del programa. O sea:


Ejemplo práctico
Veamos entonces un ejemplo concreto. Lo llamaré, el dilema del pollito.
Todos sabemos que un ave tiene distintos atributos y acciones como por ejemplo:
- Graznar
- Caminar
- Volar
Si lo pudiéramos modelar sería algo mas menos así:
Supongamos entonces que vamos a crear una nueva clase llamada Urraca, está heredará de ave, por lo que tendríamos que el urraca:
- Grazna
- Camina
- Vuela
¿Cierto?, perfecto; entonces eventualmente si yo hago

Puedo reemplazarlo perfectamente por:

Ya que si creo un ave Ave o un ave Urraca, ambos pueden graznar, caminar y volar. Si reemplazo ave por Urraca entonces no afecta la funcionalidad del programa, ergo, estamos respetando el principio.
Pero que pasa si ahora quiero crear otra ave, Pollito. en estricto rigor es un ave… ¿no?. Entonces lo lógico es que herede de Ave, quedando algo así.

Pero hay algo que no cuaja, los pollitos no vuelan.
Eventualmente si yo instancia de Ave o Pollito, existiría una diferencia y el Pollito colapsaría cuando le pida volar.

¿Que hago entonces?, bueno, puedo tener 2 soluciones:
Aves que vuelan, y aves que no vuelan, que implementen las funciones intermedias necesarias. Así ademas soportaríamos aves que nadan, corren, etc.


¿Como modelamos entonces la herencia?, es un proceso evolutivo, o sea, generalmente no es planificada. Y no me malentiendan, en un diseño orientado a objetos, es muy probable que salgamos con clases padre modeladas al principio, pero en la práctica, se hace lo siguiente:
- Programo mi clase S
- Programo mi clase S’
- Veo que existen métodos comunes entre ambos y paff nace la clase T
Que pasa si sale una clase S» que eventualmente la clase T no soporte.
Bueno, para ello deberiamos entonces distribuir la funcionalidad en S y S’, adelgazando T para que se cumpla el principio de Liskov.
Espero se haya entendido, si no, me preguntan.