Principios SOLID – Principio de Abierto/Cerrado (OCP)

En una anterior entrada, explicaba los principios SOLID y, además, hice un vídeo del primero de ellos con varios ejemplos.

En esta entrada, quiero explicar el segundo de ellos y dar a conocer la importancia de entenderlo y saberlo aplicar. Este, dice así,

Principio de Abierto – Cerrado (OCP)  toda clase debe ser extensible sin requerir modificación en la implementación de sus métodos internos. Esto se consigue mediante la abstracción.

Es decir, que una entidad (clase) en nuestro desarrollo debería estar abierta a extensión pero cerrada a modificación.
Para ver esto más claro y entender OCP más rápidamente, primero, veremos una violación del principio OCP. Dentro de un entorno profesional académico dónde tenemos diferentes perfiles y cada uno de ellos tiene variables salariales en función de su antigüedad. En un inicio, lo que haremos, será implementar este conjunto de clases,

public class Teaching {
      public double VariableSalary { get; set; }
      public double Antiquity { get; set; }
}

public class VariableSalaryCalculator {
      public double VariableSalary(IList<Teaching> personal)
      {
          double salary = 0;
          foreach(Teaching p in personal)
              salary = p.Antiquity * 1.2d;
          return salary;
      }
}

Cómo podemos observar, tenemos la clase Teacher con sus propiedades y posteriormente otra para calcular el salario variable.

Aparentemente, esto, parece correcto. El problema viene, cuando pasado un tiempo, nos solicitan que el salario variable ahora es aplicable para todo el personal relacionado con la enseñanza y no únicamente para los profesores, y que además, es diferente en función de ese rol.

Si no tenemos en cuenta este principio, nuestro código sería modificado con un resultado similar al siguiente,

public class VariableSalaryCalculator
{
    public double VariableSalary(IList<Object> teachers)
    {
        double salary = 0;
        foreach (var p in teachers)
        {
            if (p is Teacher)
            {
                Teacher pItem = (Teacher)p;
                salary = pItem.Antiquity * 1.2d;
            }
            else
            {
                Administrative pItem = (Administrative)p;
                salary = pItem.Antiquity * 1.1d;
            }
        }
        return salary;
    }
}

Cómo podemos observar, esto es incorrecto. No únicamente por el principio OCP si no porque estamos escribiendo un código altamente acoplado y una cohesión baja teniendo que modificar la clase para cada rol que tengamos.

Siguiendo el ejemplo, y para aplicar el principio OCP, tenemos una solución muy sencilla; crear una clase base que represente al personal docente y extenderlo mediante nuevas clases que sobrescriban el método según el rol del personal.

public class Teaching {
      public abstract double VariableSalary { get; set; }
      public double Antiquity { get; set; }
}

public class Teacher : Teaching {
      public override double VariableSalary {
      	salary = this.Antiquity * 1.2d;
      }
}

public class Administrative : Teaching {
      public override double VariableSalary { 
	salary = this.Antiquity * 1.1d;
      }
}

Sencillo, ¿Verdad?

Esto nos asegura que nuestro sistema se podrá extender, será fácil de mantener, de hacer Unit Testing en él… y además, cumpliremos con el principio OCP.

Happy clean code :)