Principio Abierto/Cerrado (OCP)
Tags: solid, ocp, php, cleancode
Published: 2019-07-18 11:08:01

---
title: Principio Abierto/Cerrado (OCP)
published: true
description: 
tags: Solid,Ocp,PHP,Clean Code
---

![](https://cdn-images-1.medium.com/max/2000/1*8hGsfjx9JcHoZkEAkdRSBA.png)

Siguiendo con la serie de principios SOLID, hoy hablaremos sobre el *Principio Abierto/Cerrado*, al que nos referiremos desde ahora como **OCP**.

Este principio fue enunciado por primera vez por [Bertrand Meyer](https://es.wikipedia.org/wiki/Bertrand_Meyer) en su obra de 1988 [Object-Oriented Software Construction](http://amzn.to/2x0yAij) y muy posteriormente, en 2002, Bob Martin publicó [un articulo sobre el mismo tema](http://web.archive.org/web/20060822033314/http://www.objectmentor.com/resources/articles/ocp.pdf).

El OCP, si es la primera vez que lo oyes enunciar, puede parecer una contradicción en si mismo, pero en breve veremos que la programación orientada a objetos nos da todas las herramientas para que este sea posible.

El principio OCP se define como:
> # Una clase debería estar abierta a extensión pero cerrada a modificación. Tenemos que ser capaces de extender el comportamiento de nuestras clases sin necesidad de modificar su código.

En pocas palabras, **nuestras clases deberían de ser capaces de hacer más cosas, pero sin editar su código**.

## ¿Cómo cumplimos con el OCP?

Como antes decía, puede parecer una contradicción en si mismo, pero no olvidemos que tenemos que pensar en un entorno de OOP, y debemos utilizar todos los mecanismos a nuestro alcance para conseguir esto, así que para solucionar el OCP en nuestro código debemos de usar el [**polimorfismo**](https://es.wikipedia.org/wiki/Polimorfismo_(inform%C3%A1tica)), y tenemos dos opciones igualmente válidas para implementarlo:

* Polimorfismo con Interfaces

* Polimorfismo con Clases Abstractas

Supongamos una clase *IdentityNumberValidator*, la cual tiene un método *validate(IdentityDocument $document)* encargada de decirnos si el número de identidad de un documento es válido o no:

 <iframe src="https://medium.com/media/8b2524cb5b302fe051986ecd0517c8ca" frameborder=0></iframe>

Como podemos ver, la clase *IdentityNumberValidator* no cumple con el OCP: **con cada nuevo tipo de documento que vaya a soportar nuestro sistema, esta clase debe de ser editada para dar soporte al mismo**.

Esto es ciertamente un problema, y ese **if** parece que va a crecer como la espuma… Veamos una solución usando polimorfismo con clases abstractas:

 <iframe src="https://medium.com/media/a7989cef37280b25064c824e05eed8d5" frameborder=0></iframe>

Como se puede observar:

* La clase *IdentityDocument* ahora es abstracta, y define un nuevo método *isValid* que es quien ejecutará la validación del número de identificación.

* Las clases *SpanishIdentityDocument* y *FrenchIdentityDocument* se ven forzadas a implementar el método *isValid*.

* La clase *IdentityNumberValidator* ahora únicamente llama al método *isValid* que es seguro que va a estar implementado, ya que la clase abstracta fuerza a ello. Por más tipos que se añadan a nuestro sistema, seguirá siendo capaz de validarlos, por tanto cumple con el OCP.

* **[Punto extra]** Lanzar la excepción *InvalidDocumentTypeException* ya no va a ser necesario ;) Ahora carece de sentido ya que no necesitamos comprobar el tipo de documento y ver si tenemos una validación asociada a el tipo.

Como parte negativa a esta solución, puede que cumplir con el OCP esté haciendo que no cumplamos con el [SRP](https://medium.com/all-you-need-is-clean-code/principio-de-%C3%BAnica-responsabilidad-srp-c67c3626ff47), ya que ahora las clases concretas de cada tipo de documento tienen una responsabilidad extra: *saber* cómo debe ser validado su número.

Veamos ahora una solución basada en interfaces que evite esto:

 <iframe src="https://medium.com/media/1eebab657626599c48def0a4db103c64" frameborder=0></iframe>

**¿Qué ha cambiado?**

* La lógica de la validación ahora no esta ni en el validador ni en los ValueObjects, está en un nuevo tipo de clases que llamaremos *ValidatorRule* y que implementan una interfaz común *IdentityNumberValidatorRuleInterface.*

* Ahora el validador necesita que le pasemos en su construcción una serie de reglas de validación, y él se encargará de buscar la regla adecuada o lanzar una excepción si se da el caso de que no existe ninguna.

**Ventajas**

* La lógica está mas repartida, se cumple el SRP

* El validador es más extensible, podemos instanciarlo con las reglas que queramos cada vez, pudiendo crear infinitas combinaciones, por lo tanto se cumple el OCP.

**Inconvenientes**

* La instanciación del servicio de validación es más compleja, hay que pasarle los validadores manualmente al constructor. No obstante, el uso del patrones como [Abstract Factory](https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/AbstractFactory#abstract-factory), [Factory Method](https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/FactoryMethod#factory-method), [Static Factory](https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/StaticFactory#static-factory) o [Simple Factory](https://github.com/domnikl/DesignPatternsPHP/tree/master/Creational/SimpleFactory#simple-factory) (por mencionar algunos) pueden aliviar esta carga.

* Otra forma de añadir reglas de validación sería mediante un método *addRule(IdentityNumberValidatorRuleInterface $rule)*, pero esto crearía un acoplamiento temporal en nuestro código, ya que hasta que no se haga una llamada a *addRule* nuestro validador no sirve de nada.

## ¿A qué huele una clase que no cumple con el OCP?

Ahora que ya sabemos qué es el OCP y como cumplir con él… ¿Cómo detectamos que nuestras clases no cumplen con él?

Debemos fijarnos siempre en:

* Qué clases modificamos más a menudo.

* Si cada vez que hay un nuevo requisito o una modificación de los existentes, las mismas clases se ven afectadas.

* Cada vez que añadimos un nuevo tipo de algo (moneda, idioma, método de pago…) a nuestro sistema, las mismas clases se ven afectadas.

Aunque no son motivos determinantes, nos pueden dar una pista de que algo está pasando en nuestro código.

Hasta la próxima!