Principio es Principio
Este principio es uno de los más difíciles de digerir, porque entenderlo es complicado y después de que lo entiendes, es duro de aceptar, cómo así que el software debe estar cerrado a modificaciones? acaso para eso no se creo el software? para poder fácilmente modificar un sistema, abierto para extensión? acaso cuando lo modifico no lo estoy extendiendo?.
Pero créanlo o no este principio es el corazón de mucho de los conceptos de la programación orientada a objetos, como encapsulamiento, el no uso de variables globales, para mas información ir al siguiente link.
Este es el tercer post de una saga para explicar los principios SOLID.Ver el primer Post Ver el segundo Post
Este principio es uno de los más difíciles de digerir, porque entenderlo es complicado y después de que lo entiendes, es duro de aceptar, cómo así que el software debe estar cerrado a modificaciones? acaso para eso no se creo el software? para poder fácilmente modificar un sistema, abierto para extensión? acaso cuando lo modifico no lo estoy extendiendo?.
Pero créanlo o no este principio es el corazón de mucho de los conceptos de la programación orientada a objetos, como encapsulamiento, el no uso de variables globales, para mas información ir al siguiente link.
Este es el tercer post de una saga para explicar los principios SOLID.Ver el primer Post Ver el segundo Post
Un Requerimiento
El sistema debe calcular el salario neto de un empleado de acuerdo a la sigueinte formula:
Salario Neto = SalarioBruto - SalarioBruto*0.08 + Bonificaciones
Donde las Bonificaciones se calculan así:
Si el tipo de trabajador es JUNIOR: Bonificaciones = Ventas*0.02
Si el tipo de trabajador es SENIOR:
Si Ventas > 1000 y Ventas < 10000 Bonificaciones = Ventas*0.03
Si Ventas > 10001 y Ventas < 20000 Bonificaciones = Ventas*0.1 + 5000
Si Ventas > 20001 Bonificaciones = Ventas*0.2 + 15000
Solución 1
public class Empleado { public string Nombre; public TipoEmpleado Tipo; public double Salario; public double Ventas; } public enum TipoEmpleado { JUNIOR, SENIOR } public class PagoEmpleado { public double ObtenerSalarioNeto(Empleado empleado) { double bonificacion = 0; switch (empleado.Tipo) { case TipoEmpleado.JUNIOR: bonificacion = empleado.Ventas * 0.02; break; case TipoEmpleado.SENIOR: { if (empleado.Ventas > 1000 && empleado.Ventas < 10000) bonificacion = empleado.Ventas * 0.03; else if (empleado.Ventas > 10000 && empleado.Ventas < 20000) bonificacion = empleado.Ventas * 0.1 + 5000; else if (empleado.Ventas > 20000) bonificacion = empleado.Ventas * 0.2 + 15000; } break; default: throw new NotImplementedException("Tipo de Empleado no implementado."); } double salarioNeto = empleado.Salario - empleado.Salario * 0.08 + bonificacion; return salarioNeto; } }
Solución 2
public abstract class Empleado { public string Nombre; public double Salario; public double Ventas; public abstract double CalcularBonificacion(); } public class EmpleadoJunior:Empleado { public override double CalcularBonificacion() { return Ventas * 0.02; } } public class EmpleadoSenior : Empleado { public override double CalcularBonificacion() { double bonificacion = 0; if (Ventas > 1000 && Ventas <= 10000) bonificacion = Ventas * 0.03; else if (Ventas > 10001 && Ventas <= 20000) bonificacion = Ventas * 0.1 + 5000; else if (Ventas > 20000) bonificacion = Ventas * 0.2 + 15000; return bonificacion; } } public class PagoEmpleado { public double ObtenerSalarioNeto(Empleado empleado) { double salarioNeto = empleado.Salario - empleado.Salario * 0.08 + empleado.CalcularBonificacion(); return salarioNeto; } }
Nuevo Requerimiento.
Si el tipo de trabajador es EXPERTO:
Si Ventas < 1000 Bonificaciones = Ventas*0.04
Si Ventas > 1000 y Ventas < 10000 Bonificaciones = Ventas*0.1 + 5500
Si Ventas > 10000 y Ventas < 20000 Bonificaciones = Ventas*0.25 + 10000
Si Ventas > 20000 Bonificaciones = Ventas*0.35 + 35000
Modificando la solución 1
Para aplicar el nuevo requerimiento y conservar la misma estructura de la solución 1 lo que debemos hacer es agregar el valor EXPERTO la enumeración TipoEmpleado y en el Switch del método CalcularSalarioNeto adicionar un caso mas para el nuevo tipo EXPERTO:
public enum TipoEmpleado { JUNIOR, SENIOR, EXPERTO } case TipoEmpleado.EXPERTO: { if (empleado.Ventas <= 1000) bonificacion = empleado.Ventas * 0.04; else if (empleado.Ventas > 1000 && empleado.Ventas <= 10000) bonificacion = empleado.Ventas * 0.1 + 5500; else if (empleado.Ventas > 10000 && empleado.Ventas <= 20000) bonificacion = empleado.Ventas * 0.25 + 10000; else if (empleado.Ventas > 20000) bonificacion = empleado.Ventas * 0.35 + 35000; } break;
Vemos que la solución 1 bajo la luz del nuevo requerimiento el método CalcularSalarioNeto no está Abierto/Cerrado ya que debimos modificarlo ante la existencia de una nueva regla, debimos modificar el código del método CalcularSalarioNeto.
"Modificando" la solución 2
"Modificando" la solución 2
Para aplicar el nuevo requerimiento y conservar la misma estructura de la solución 2 lo que debemos hacer es agregar una nueva Clase llamada EmpleadoExperto que al igual que las clases EmpleadoJunior y EmpleadoSenior debe heredar de la clase abstracta Empleado y sobreescribir el método CalcularSalarioNeto con la regla del nuevo requerimiento:
public class EmpleadoSenior : Empleado { public override double CalcularBonificacion() { double bonificacion = 0; if (Ventas <= 1000) bonificacion = Ventas * 0.04; else if (Ventas > 1000 && Ventas <= 10000) bonificacion = Ventas * 0.1 + 5500; else if (Ventas > 10000 && Ventas <= 20000) bonificacion = Ventas * 0.25 + 10000; else if (Ventas > 20000) bonificacion = Ventas * 0.35 + 35000; return bonificacion; } } public class PagoEmpleado { public double ObtenerSalarioNeto(Empleado empleado) { double salarioNeto = empleado.Salario - empleado.Salario * 0.08 + empleado.CalcularBonificacion(); return salarioNeto; } }
Vemos que la solución 2 bajo la luz del nuevo requerimiento, el método CalcularSalarioNeto está Abierto/Cerrado ya que no debimos modificarlo y extendimos su funcionalidad con código nuevo.
Decidiendo que es Abierto/Cerrado, un nuevo requerimiento.
Pero la solución 2 no es del todo Abierto/Cerrado, porque y si hay un nuevo requerimiento? como por ejemplo que el salario neto debe calcularse de una nueva manera, bajo la Luz de este nuevo requerimiento la solución 2 no es Abierto/Cerrado ya que deberiamos modificar el código del método CalcularSalarioNeto.
Pero entonces que hacer? cuando debo aplicar el principio?
Para decidir que es Abierto/Cerrado, abstracción es la clave, para obtener los beneficios de la programación orientada a objetos como mantenibilidad, reusabilidad entre otros, pero sin duda alguna la experiencia en el diseño orientado a objetos y el conocimiento del negocio juegan un papel clave para anticiparse a esos posibles cambios que siempre son inevitables, ya que no todo puede ser Abierto/Cerrado.
Fuentes:
http://www.objectmentor.com/resources/articles/ocp.pdf
http://www.amazon.com/Agile-Principles-Patterns-Practices-C/dp/0131857258/ref=sr_1_1?ie=UTF8&qid=1315594440&sr=8-1
Happy Codding
The Clean Coder, Because small things matter.
Para decidir que es Abierto/Cerrado, abstracción es la clave, para obtener los beneficios de la programación orientada a objetos como mantenibilidad, reusabilidad entre otros, pero sin duda alguna la experiencia en el diseño orientado a objetos y el conocimiento del negocio juegan un papel clave para anticiparse a esos posibles cambios que siempre son inevitables, ya que no todo puede ser Abierto/Cerrado.
Fuentes:
http://www.objectmentor.com/resources/articles/ocp.pdf
http://www.amazon.com/Agile-Principles-Patterns-Practices-C/dp/0131857258/ref=sr_1_1?ie=UTF8&qid=1315594440&sr=8-1
Happy Codding
The Clean Coder, Because small things matter.


No hay comentarios:
Publicar un comentario