Logotipo SOLID
Getting your Trinity Audio player ready...

En esta segunda parte de los principio SOLID, vamos a ver el llamado principio abierto/cerrado (open/closed).

Introducción.

Este principio fue formulado por uno de los grandes Bertrand Meyer, en su libro Object-Oriented Software Construction. Si nos vamos a la definición que hay dada sería:

Las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas para su extensión, pero cerradas para su modificación.

Bertrand Meyer

Cuando un cambio en una aplicación provoca cambios en cascada en múltiples módulos interdependientes que no deberían ser modificados, esto indica un problema de diseño en el programa. Este acoplamiento excesivo hace que cada cambio sea una tarea difícil y compleja, especialmente en proyectos grandes.

El principio "Open/Closed" nos insta a desarrollar elementos de software que realicen su función sin la necesidad de modificaciones en el futuro. Al seguir este principio, podemos evitar modificar código que funciona correctamente y evitar la introducción de errores innecesarios.

La implementación de este principio requiere una planificación cuidadosa y una arquitectura adecuada para asegurar que los elementos de software estén diseñados para ser extensibles sin necesidad de ser modificados. De esta manera, podemos reducir la complejidad del código y mejorar la capacidad de mantenimiento y extensibilidad del software.

Ejemplo.

Comencemos con el siguiente código en TypeScript. Aunque los conceptos son genéricos, aquellos familiarizados con lenguajes como Java, C++, o C# encontrarán la sintaxis similar.

En este ejemplo, tenemos un controlador de dispositivos inteligentes capaz de ejecutar los comandos de encendido y apagado de dispositivos. En un mundo ideal, hasta el momento, ha funcionado correctamente con todos los dispositivos probados:

class Device {
  public on() {
    this.deviceProtocol.executeOn();
  }
  
  public off() {
    this.deviceProtocol.executeOff();
  }
}

Pero llega un aciago día en el que nos toca controlar un nuevo tipo de dispositivo inteligente, por ejemplo una estufa. Y en este caso, para apagarla, primero es necesario ponerla en modo suspensión. Debido a esta nueva funcionalidad, es necesario realizar un cambio en nuestro código anterior:

class Device {
  public on(): void {
    this.deviceProtocol.on();
  }
  
  public off(): void {
    if (this.deviceType === "stove") {
      this.deviceProtol.executeSuspend();
    }
    this.deviceProtocol.executeOff();
  }
}

En este caso, hemos tenido que modificar el código debido a un único caso adicional, lo cual va en contra del principio de diseño que estamos tratando de seguir. De acuerdo con éste, debemos evitar realizar modificaciones en el código existente. En su lugar, debemos asegurarnos de que nuestras clases estén abiertas para su extensión.

Es por ello, que para resolver este problema podemos hacer uso de la herencia. En lugar de modificar la clase existente, crearemos una clase que herede de Device y extienda su funcionalidad para manejar la estufa:

class Device {
  public on(): void {
    this.deviceProtocol.on();
  }
  
  public off(): void {
    this.deviceProtol.executeSuspend();
  }
}

class StoveDevice extends Device {
  public off(): void {
    this.deviceProtol.executeSuspend();
    super.off();
  }
}

De esta manera, cumplimos con el principio abierto/cerrado. La clase original permanece intacta y cerrada para su modificación, mientras que hemos creado una nueva clase que extiende su funcionalidad y está abierta para su extensión. Esto nos ha permitido modificar código que ya funcionaba correctamente y había sido probado exhaustivamente.

Imaginemos lo que habría sucedido en el caso de haber modificado la clase original para incluir la funcionalidad adicional requerida: si hubiésemos cometido un error en ese proceso, no solo la estufa no se apagaría correctamente, sino que también todos los dispositivos que antes se apagaban correctamente quizás ahora hubieran dejado de hacerlo.

Es importante tener en cuenta que, aunque este principio no impide que en el futuro tengamos que hacer cambios en la clase, estos cambios deberían estar orientados principalmente a solucionar errores que puedan surgir, en lugar de añadir funcionalidades que la clase original no debería manejar.

Conclusión.

El principio abierto/cerrado es esencial para el desarrollo de software de calidad. Al seguir este principio, podemos diseñar elementos de software que sean flexibles y extensibles sin comprometer su estabilidad o eficacia, lo que resulta en un código más limpio y fácil de mantener.

Y aquí terminamos la explicación del segundo principio SOLID. Como siempre, cualquier duda, sugerencia o corrección, será bienvenida. Nos vemos en el capítulo L (seguro que a quien sea fan de Death Note le ha hecho ilusión el nombre).

Puedes ver un resumen en esta entrada de Instagram.

Más artículos de esta serie:
Capítulo S: Single Responsibility Principle
Capítulo L: Liskov Substitution Principle
Capítulo I: Interface-Segregation Principle
Capítulo D: Dependency Inversion Principle

Comparte este artículo con quien quieras
Principios SOLID. Capítulo S: Single Responsibility Principle
Principios SOLID. Capítulo L: Liskov Substitution Principle

Leave a Comment

Your email address will not be published. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.