Haz de tus aplicaciones una roca sólida | Liskov Substitution

Teoría

Sí, lo se. Seguramente ya han oído sobre este principio un montón de veces hasta hoy y probablemente lo que significa también. Si le preguntas a diferentes colegas qué significa obtendrías diferentes respuestas o quizás, en vez de una respuesta… una definición.

«El principio de Liskov Substitution es sobre herencia»

Bueno… sí, algo sobre herencia tiene que ver… pero no es sólo herencia. Probemos con algún otra definición:

“El principio de Liskov Substitution nos dice que deberíamos ser capaces de intercabiar objetos de la misma ‘familia’ “

Bueno, esto también es verdad aunque tampoco es sólo eso.

Si recuerdan en la introducción del primer capítulo de esta serie – que si todavía no lo hicieron les recomiendo leer – decía algo así:

«Si estamos construyendo un sistema con partes intercambiables, esas partes deben acordar un contrato que les permita ser sustituídas por otras»

Esta última definición me gusta un poco más que las anteriores básicamente porque lo que nos dice es que si existe un contrato ( interface o una abstract class con template methods) y a su vez tenemos varios objetos que se suscriben a ese contrato, sin importar dónde usemos y cómo esos objetos, si los intercambiamos el comportamiento esperado debería ser… bueno, esperado.

¿Qué les parece si revisamos un ejemplo para clarificar todo esto?


La interfaz List de Java y el método sort de Collections

Uno de los casos más desconcertantes sobre la fractura de Liskov Substitution es la función sort en la clase Collections de Java el cual descubrí por supuesto recibiendo un unexpected error (?) en una aplicación que estaba construyendo.

Para poder ordenar una colección de elementos en Java, es necesario usar el método helper en la clase Collections que toma una instancia de una List<T> y la instancia de un Comparator (Kotlin resuelve esto mucho más fácilmente con sortBy()):

Esto significa que podríamos pasar cualquier objeto que respete la interfaz de List<T>, donde T es el tipo concretos que integran la lista. Si tuviéramos en nuestra aplicación Android un Array a medida y necesitáramos convertirlo en una List, probablemente haríamos algo así:

Todo perfecto! Hemos creado una lista de String. Si la intentamos ordenar obtendríamos un hermoso Crash!

UnsupportedOperationException was thrown

En principio, es probable que no veamos cuál es el problema aquí. De hecho, ¿qué problema podría haber con ordenar esta lista? Sin embargo, investigando un poco en la documentación de ambos ( Arrays.asList() y Collections.sort(), podremos ver que la operación sort puede lanzar una excepción como la anterior si la lista provista no soporta la operación de agregado y Array.asList() devuelve «una lista de tamaño fijo»

La lista retornada por el método asList() no cumple con el principio Liskov Substitution debido a que List<T> no es reemplazable en el contexto de esta operación.
Collections.sort(..) espera CUALQUIER instancia de un List<T> y nosotros le proveímos una instancia la cula no cumple completamente el protocolo de List<T>. Por supuesto, implementamos el protocolo pero la operación de add está «bloqueada» por una excepción lanzada en su llamada.

Conclusión

Éste es sólo un caso de los muchos ejemplos que podremos encontrar en el código de java y seguramente en muchos otros lenguajes. En este caso, «no tuvimos nada que ver» dado que no fue nuestra responsabilidad. Sin embargo, es importante evitar este tipo de jerarquía de clases en nuestras aplicaciones a la hora de evitar cualquier comportamiento inesperado en runtime y un diseño pobre.

Notemos que, a pesar de que LSP nos habla sobre herencia, el ejemplo que presentamos anteriormente es sobre protocolos en vez de ello. Lo importante es, sin importar si estamos usando una interface o una abstract class, estamos definiendo un contrato y ese contrato debe ser respetado por las clases que se encuentren implementadas actualmente o las futuras.

En el próximo post hablaremos sobre el próximo de los principios de nuesttra serie de SOLID, el principio de Interface Segregation.

Sigamos conectados 😄
Happy Coding! ✍️

Cover photo by CHRIS BRUNSKILL/FANTASISTA/GETTY IMAGES

0

Deja un comentarioCancelar respuesta