Bienvenidos sean a este post, hoy veremos uno de los principios de SOLID.
Este principio trata sobre como heredar de un tipo de una manera correcta. Es decir, si tenemos una funcion que toma un argumento de un tipo, luego la misma funcion debe tomar de algun tipo derivado.
Una vez que se entiende a herencia y el principio de substitucion Liskov, nos seran muy dificil de olvidar. Continuemos con la clase Producto que vimos en el post anterior, y ahora agreguemos un nuevo metodo que devuelva el valor del producto en base a la moneda local. Podemos almacenar el precio en la misma unidad monetaria y crear un metodo para convertirla a una moneda especifica. Veamos como puede ser su implementacion:
enum class Moneda { USD, EUR, ARS };
class Producto
{
// codigo omitido por brevedad
double convertir_precio(Moneda m) {
// convertimos al precio adecuado
}
private:
std::string nombre_;
double precio_;
};
Primero definiremos un enum para representar los distintos tipos de monedas, para este ejemplo solo usamos tres pero puede haber muchos mas, En la parte publica, agregamos un metodo para convertir el precio del producto a la moneda que informemos. Vamos a suponer que ahora se ofrece de manera fija un descuento del 12% a los productos digitales. Para ello, modifiquemos a la clase ProductoDigital de la siguiente manera:
class ProductoDigital : public Producto
{
void set_path(std::string);
std::string get_path();
double convertir_precio_con_descuento(Moneda m) {
// convertir pero aplicando el descuento
}
private:
std::string path_;
};
Esta es la solucion mas obvia, no? pero tenemos un inconveniente dado que llamar a convert_precio desde aqui no tendra ningun efecto. Y peor aun, no debe llamarlo sino que debe utilizar al nuevo metodo, para poder tener el descuento que se aplica a este tipo de productos. Esto contradice lo que dice el principio, en lugar de hacer este cambio realicemos el siguiente:
enum class Moneda { USD, EUR, ARS };
class Producto
{
// codigo omitido por brevedad
virtual double convertir_precio(Moneda m) {
// implementacion predeterminada
}
private:
std::string nombre_;
double precio_;
};
class ProductoDigital : public Producto
{
void set_path(std::string);
std::string get_path();
double convertir_precio(Moneda m) override {
// convertir pero aplicando el descuento
}
private:
std::string path_;
};
Esta es la belleza del polimorfismo, ya no necesitamos al nuevo metodo sino simplemente lo anulamos (override) en la clase heredera. En la clase base, el metodo sufrio un unico cambio. Lo hicimos virtual, el resto debe seguir siendo lo mismo. Con la anulacion, del codigo en la clase heredera ya tenemos el metodo para el resto de las otras clases y para esta especificamennte. Y si bien, ahora cumplimos con el principio esto se puede mejorar. Veamos el siguiente codigo:
class Producto
{
// codigo omitido por brevedad
virtual double convertir_precio(Moneda m) {
auto precio_final = aplicar_descuento();
// conversionn a partir del valor de final_precio
}
private:
virtual double aplicar_descuento() {
return get_precio();
}
std::string nombre_;
double precio_;
};
Primero hablemos del metodo en cuestion en la parte publica. Este ahora tiene una variable donde almacenara el valor devuelto por el metodo aplicar_descuento, despues hacemos toda la operacion en base al valor de la variable. En la parte privada definiremos a aplicar_descuento, esta es tambien virtual y devuelve el valor del producto de manera predeterminada. Es decir, sin ningun tipo de descuento ni nada que se le asemeje. Pasemos a la clase ProductoDigital y hagamos el siguiente cambio:
class ProductoDigital : public Producto
{
void set_path(std::string);
std::string get_path();
private:
double aplicar_descuento() override {
return get_precio() * 0.12;
}
std::string path_;
};
Eliminamos la nueva definicion del metodo en la parte publica pero la realizamos en la parte privada. Observen que en este caso seguimos devolviendo el precio pero ahora le aplicamos el descuento. Una de las ventajas de trabajar de esta forma, es que las funciones privadas no puede ser accedidas desde afuera pero si pueden ser anuladas o sobrecargadas desde clase herederas o derivadas. Como solamente esta clase necesita aplicar el descuento, solo se implementa en esta dejando la interfaz completamente sin tocar.
El proceso de diseño es creativo y en muchas ocasiones inmisericorde. Porque no es raro que tengamos que reescribir todo el codigo por un nuevo requerimiento inesperado. Por esta razon, se utilizan principios y enfoques para minimizar el impacto de implementar nuevas caracteristicas en nuestro proyecto.
En resumen, hoy hemos visto al Principio substitucion de Liskov, Liskov substitution Principle, que es, para que sirve, como se aplica, asi como una serie de ejemplos para entender el concepto de utilizarlo junto a polimorfismo. Espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
