Este es el segundo de una serie de cinco principios SOLID y su aplicación en la Programación Orientada a Objetos.
odas las aplicaciones cambian durante su ciclo de vida, y siempre vendrán nuevas versiones tras la primera release. No por ello debemos adelantarnos a desarrollar características que el cliente podría necesitar en el futuro; si nos pusiéramos en el papel de adivinos, seguramente fallaríamos y probablemente desarrollaríamos características que el cliente nunca necesitará. El principio YAGNI ("You Aint Gonna Need It" o "No vas a necesitarlo"), utilizado en la Programación Extrema, previene de implementar nada más que lo que realmente se requiera. La idea es desarrollar ahora sobre los requisitos funcionales actuales, no sobre los que supongamos que aparecerán dentro de un mes.
La actitud de adelantarnos a los acontecimientos es un mecanismo de defensa que en ocasiones acusamos los desarrolladores para prevenir lo que tarde o temprano será inevitable: la modificación. Lo único que podemos hacer es minimizar el impacto de una futura modificación en nuestro sistema, y para ello es imprescindible empezar con un buen diseño, ya que la modificación de una clase o módulo de una aplicación mal diseñada generará cambios en cascada sobre las clases dependientes que derivarán en unos efectos indeseables. La aplicación se convierte, así, en rígida, impredecible y no reutilizable.
Ahora bien, ¿cómo debemos plantear nuestras aplicaciones para que se mantengan estables ante cualquier modificación?
El Principio Open/Closed
El Principio Open/Closed (Open/Closed Principle, OCP) fue acuñado por el Dr. Bertrand Meyer en su libro "Object Oriented Software Construction" y afirma que:Una clase debe estar abierta a extensiones, pero cerrada a las modificaciones.
OCP es la respuesta a la pregunta que hacíamos anteriormente, ya que argumenta que deberíamos diseñar clases que nunca cambien, y que cuando un requisito cambie, lo que debemos hacer es extender el comportamiento de dichas clases añadiendo código, no modificando el existente.
Las clases que cumplen con OCP tienen dos características:
- Son abiertas para la extensión; es decir, que la lógica o el comportamiento de esas clases puede ser extendida en nuevas clases.
- Son cerradas para la modificación, y por tanto el código fuente de dichas clases debería permanecer inalterado.
Listado 1
public void Finalizar() { switch (_estadoTarea) { case EstadosTarea.Pendiente: // finalizamos break; case EstadosTarea.Finalizada: throw new ApplicationException("Tarea ya finalizada"); case EstadosTarea.Cancelada: throw new ApplicationException("Imposible finalizar. Tarea cancelada"); default: throw new ArgumentOutOfRangeException(); } } |
Un cambio típico solicitado por el cliente de la aplicación sería la adición de un nuevo estado para controlar las tareas que se han pospuesto, con lo que la adaptación a esta modificación podría ser la expuesta en el listado 2. Aparentemente, parece una modificación trivial; sin embargo, este cambio puede replicarse en otros métodos o clases que utilicen la enumeración EstadosTarea, de forma que en nuestro caso también deberíamos modificar el método Cancelar (listado 3).
Listado 2
public void Finalizar()
{
switch (_estadoTarea)
{
case EstadosTarea.Pendiente:
// finalizamos
break;
case EstadosTarea.Finalizada:
throw new ApplicationException("Tarea ya finalizada");
case EstadosTarea.Cancelada:
throw new ApplicationException("Imposible finalizar. Tarea cancelada");
case EstadosTarea.Pospuesta:
throw new ApplicationException("Imposible finalizar. Tarea no completada");
default:
throw new ArgumentOutOfRangeException();
}
}
Listado 3
public void Cancelar()
{
switch (_estadoTarea)
{
case EstadosTarea.Pendiente:
// cancelamos
_estadoTarea = EstadosTarea.Cancelada;
break;
case EstadosTarea.Finalizada:
throw new ApplicationException("Imposible cancelar. Tarea finalizada");
case EstadosTarea.Cancelada:
throw new ApplicationException("Tarea ya cancelada");
case EstadosTarea.Pospuesta:
// cancelamos
_estadoTarea = EstadosTarea.Cancelada;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
En definitiva, por cada nuevo estado que implementemos tendremos que identificar todas las clases que lo utilizan (tanto la clase Tarea como las clases lógicamente involucradas) y modificarlas, violando no únicamente OCP sino también el Principio DRY ("Dont Repeat Yourself", "No te repitas"), otro principio que pretende reducir al máximo cualquier tipo de duplicación. En este tipo de modificaciones existe una alta probabilidad de olvidar modificar algún método relacionado con el nuevo estado implementado en el enumerador EstadosTarea, lo que elevaría la probabilidad de aparición de un nuevo bug.
En el siguiente artículo sobre el principio Open/Closed continuamos con la explicación y finalizamos con un ejemplo.
José Miguel Torres
MVP de Device Application Development