No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aplicación del Principio de Sustitución de Liskov en Código SOLID

10/16
Recursos

¿Cómo aplicar correctamente el principio de sustitución de Liskov?

El principio de sustitución de Liskov es uno de los pilares fundamentales en la programación orientada a objetos y uno de los principios SOLID. Su aplicación garantiza que las clases derivadas puedan reemplazar sin problemas a sus clases base o superclases sin alterar el comportamiento esperado del programa. En otras palabras, las instancias de subclases deben comportarse de manera coherente con lo esperado del tipo base. Esto asegura flexibilidad y funcionalidad en nuestros proyectos de software.

¿Qué problemas existían en el código original?

El caso presentado describe una aplicación con dos tipos de empleados: contractor y full-time. Había ciertos problemas fundamentales en la estructura del código:

  • Propiedad de horas extras inadecuadamente generalizada: La clase base Employee contenía una propiedad de horas extras que solo es relevante para empleados full-time, no para contractors. Esto viola el principio porque un subtipo no debería llevar propiedades o métodos de los cuales no haga uso.

  • Cálculo de salario genérico: Un método pretendía calcular el salario de todos los tipos de empleados sin considerar sus diferencias. Esto impide que el sistema pueda escalar para nuevos tipos de empleados y es una clara violación del principio de responsabilidad única.

¿Cómo se debe reestructurar la clase Employee?

Para solucionar estos problemas, es esencial hacer algunos cambios en el diseño de las clases:

  1. Eliminar la propiedad de las horas extras de la clase base Employee. Esto se debe a que no todos los subtipos hacen uso de esta propiedad.

    public abstract class Employee {
        public string FullName { get; set; }
        public int HoursWorked { get; set; }
        public abstract decimal CalculateSalary();
    }
    
  2. Mover la implementación del cálculo de salario a cada una de las subclases, para que cada una se encargue de su método de cálculo específico.

    public class EmployeeFullTime : Employee {
        public int ExtraHours { get; set; }
    
        public override decimal CalculateSalary() {
            return (HoursWorked + ExtraHours) * 50;
        }
    }
    
    public class EmployeeContractor : Employee {
        public override decimal CalculateSalary() {
            return HoursWorked * 40;
        }
    }
    

¿Cuáles son los beneficios de aplicar el principio de Liskov?

Al reestructurar el código según el principio de sustitución de Liskov, se obtiene un sistema más flexible y escalable:

  • Reutilización y escalabilidad: Introducir nuevos tipos de Employee es más sencillo y libre de conflictos porque cada subtipo es responsable de su comportamiento específico.

  • Código más limpio y mantenible: Elimina las verificaciones y condicionales innecesarias en la clase base, lo que simplifica el código y reduce la complejidad.

  • Cumplimiento de expectativas de la superclase: Los subtipos pueden sustituirse por la clase base sin alterar el comportamiento del programa, lo que reduce errores en el sistema.

¿Cómo verificar nuestras implementaciones?

Siempre es buena práctica, después de integrar tales cambios, realizar pruebas de compilación y de ejecución para asegurar que el sistema sigue siendo funcional y se comporta según lo esperado.

  1. Compilar el código para revisar que no haya errores de sintaxis.

    dotnet build
    
  2. Ejecutar el programa para validar los resultados reales.

    dotnet run
    

Al seguir estos pasos, te aseguras de que tu sistema cumpla con el principio de sustitución de Liskov, lo que traerá como resultado un código más robusto y preparado para aumentar su complejidad de manera controlada. ¡Continúa con tu aprendizaje en SOLID y sigue mejorando tus habilidades en el desarrollo de software!

Aportes 10

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Buenas, en la clase EmployeeFullTime es necesario inicializar la propiedad de ExtraHours en el constructor de ExployeeFullTime.

namespace Liskov
{
    public class EmployeeFullTime : Employee
    {
        public int ExtraHours {get;set;}
        public EmployeeFullTime(string fullname, int hoursWorked, int extrahours) : base(fullname, hoursWorked)
        {
            ExtraHours = extrahours;
        }

        public override decimal CalculateSalary()
        {
            decimal hourValue = 50;
            return hourValue * (HoursWorked + ExtraHours);
        }
    }
}

Desde mi perspectiva esta curso vale oro.

Solo como una opinión, me gustaría que el código se mostrara multilínea para que se evite el uso del scroll horizontal y los alumnos podamos ver en una sola pantalla toda la línea del código

En el foreach del archivo de Program.cs, estaría bueno cambiar el nombre del "item" por "itemEmployee" para que el nombre sea más descriptivo

Chat GPT
¡Claro! El Principio de Sustitución de Liskov (Liskov Substitution Principle, LSP) es uno de los cinco principios SOLID y fue propuesto por Barbara Liskov en 1987. Este principio se centra en la relación entre una clase base (o supertipo) y sus clases derivadas (o subtipos). La idea fundamental es que los objetos de una clase derivada deben poder ser usados en lugar de objetos de la clase base sin afectar la corrección del programa.

En términos más simples, si tienes una clase base (supertipo) y una clase derivada (subtipo), deberías poder sustituir una instancia de la clase base con una instancia de la clase derivada sin que el programa deje de funcionar correctamente.

Aquí hay una explicación más detallada:

  1. Relación de herencia:

    • Imagina que tienes una clase base llamada Figura y una clase derivada llamada Círculo. Según el LSP, deberías poder utilizar un objeto de tipo Círculo en cualquier lugar donde se espera un objeto de tipo Figura, ya que Círculo es un subtipo de Figura.
  2. Comportamiento consistente:

    • Las clases derivadas deben comportarse de manera consistente con la clase base. Esto significa que los métodos de la clase base deben poder ser invocados en instancias de la clase derivada, y el resultado debería ser coherente con lo que se espera de la clase base.
  3. No alterar la invariante del tipo:

    • La invariante del tipo es cualquier propiedad que debe mantenerse verdadera para todas las instancias de un tipo. El LSP establece que las clases derivadas no deben alterar la invariante del tipo establecida por la clase base.
  4. Pre y Post condiciones:

    • Cuando un método de la clase base tiene ciertas precondiciones (condiciones que deben ser verdaderas antes de que se llame al método) y postcondiciones (condiciones que deben ser verdaderas después de que el método se haya ejecutado), estas condiciones también deben mantenerse en las clases derivadas.

En resumen, el Principio de Sustitución de Liskov busca garantizar que las clases derivadas extiendan o especialicen el comportamiento de la clase base sin romper la lógica del programa. Esto facilita la creación de sistemas más flexibles y extensibles, ya que las nuevas clases pueden ser introducidas y utilizadas sin afectar el código existente que trabaja con la clase base.

También podemos pasar la propiedad HourValue a la clase Employee, agregarla al constructor de esa clase abstracta, y cada subclase pasarlo directo sin necesidad que esta subclase lo pida en su constructor.

public Employee(string fullname, int hoursWorked, int hourValue)
        {
            Fullname = fullname;
            HoursWorked = hoursWorked;
            HourValue = hourValue;
        }
public EmployeeFullTime(string fullname, int hoursWorked, int extraHours) : base(fullname, hoursWorked, 50)
        {
            this.ExtraHours = extraHours;
        }
![](https://static.platzi.com/media/user_upload/image-cc483950-cf13-4f99-85f6-608a0a221c7e.jpg)
Este codigo es otro ejemplo aplicando interfaces interface IBird { void Eat(); } interface IFlyableBird : IBird { void Fly(); } class Sparrow : IFlyableBird { public void Eat() { Console.WriteLine("The sparrow is eating."); } public void Fly() { Console.WriteLine("The sparrow is flying."); } } class Penguin : IBird { public void Eat() { Console.WriteLine("The penguin is eating."); } } class Program { static void Main() { IFlyableBird flyingBird = new Sparrow(); flyingBird.Fly(); // Funciona bien ✅ IBird bird = new Penguin(); bird.Eat(); // No se espera que vuele, solo come ✅ } }
Tengo una duda: Si pasamos la propiedad ExtraHours a la clase FullTime que pasaría si viene otro tipo de empleador que necesite también ExtraHours, estaríamos violando el principio de Liskov y OpenClose debido que tendríamos que modificar la clase EmployeeFullTime o en el mejor de los casos crear otra clase.
Este principio nunca me habia quedado del todo claro pero con esta aplicacion me funciono bastante bien y ahora entiendo la funcionalidad del principio