Principio Open-Closed en Swift

En este post vamos a ver un ejemplo de diseño que no cumple con el principio Open-Closed y una versión del mismo ejemplo que sí cumple con el principio.

Principio Open-Closed (OCP)

Los principios SOLID son cinco principios básicos del diseño y la programación orientada a objetos, uno de los cuales es el principio Open-Closed

Una descripción más detallada de este principio la pueden encontrar en un artículo de Robert C. Martin “Uncle Bob”, coautor del Manifiesto Ágil, titulado The Open-Closed Principle.

Bertrand Meyer acuñó este principio en 1988 de la siguiente manera:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Y en su artículo, Uncle Bob nos explica que:

Los módulos que cumplen con el principio open-closed tienen dos atributos principales:

  1. Están “abiertos para extensiones” (Open).
    • Esto significa que el comportamiento de un módulo puede ser extendido. Que podemos hacer que un módulo se comporte de nuevas y diferentes maneras a medida en que los requerimientos de la aplicación cambien, o para satisfacer nuevas aplicaciones.
  2. Están “cerrados para modificaciones” (Closed).
    • El código fuente de dicho módulo es inviolable. Nadie está autorizado a cambiar dicho código.

Ejemplo que no cumple con OCP

Vamos primero a ver un ejemplo que no cumple con OCP. El ejemplo es una “aplicación” que permite calcular el área que ocupan un grupo de figuras en un plano (el siguiente código puede ser ejecutado en Xcode Playground):

import Foundation
class Figura {
}
class Rectangulo: Figura {
var alto: Double
var largo: Double
init(alto: Double, largo: Double) {
self.alto = alto
self.largo = largo
}
}
class Circulo: Figura {
var radio: Double
init(radio: Double) {
self.radio = radio
}
}
class CalculadorDeAreas {
var figuras: [Figura]
init(figuras: [Figura]) {
self.figuras = figuras
}
private func calcularAreaRectangulo(rectangulo : Rectangulo) -> Double {
return rectangulo.alto * rectangulo.largo
}
private func calcularAreaCirculo(circulo : Circulo) -> Double {
let pi = 3.14159
return pi * pow(circulo.radio, 2)
}
func calcular() -> Double {
var area = 0.0
for figura in self.figuras {
if (figura is Rectangulo) {
area += calcularAreaRectangulo(figura as! Rectangulo)
} else {
area += calcularAreaCirculo(figura as! Circulo)
}
}
return area
}
}
let figuras = [Rectangulo(alto: 3, largo: 4), Circulo(radio: 5)]
let calculadorDeAreas = CalculadorDeAreas(figuras: figuras)
print("El área es \(calculadorDeAreas.calcular())")

view raw
no-OCP.swift
hosted with ❤ by GitHub

Como podemos ver en el código anterior, la función calcular y la clase CalculadorDeAreas no cumple con OCP debido a que si queremos tener otros tipos de figuras en nuestra aplicación, y que podamos utilizarlas en el cálculo del área debemos modificar el código de la clase CalculadorDeAreas para introducir métodos para calcular las áreas de estos nuevos tipos de figuras, pero aún resolviendo este problema, tendríamos que modificar el método calcular para agregar nuevos if-else que tengan en cuenta los nuevos tipos de figuras.

¿Cómo podemos refactorizar el código anterior para hacerlo cumplir con OCP?

Ejemplo que cumple con OCP

El siguiente código es una refactorización del ejemplo anterior:

import Foundation
protocol TieneArea {
func calcularArea() -> Double
}
class Rectangulo: TieneArea {
var alto: Double
var largo: Double
init(alto: Double, largo: Double) {
self.alto = alto
self.largo = largo
}
func calcularArea() -> Double {
return alto * largo
}
}
class Circulo: TieneArea {
var radio: Double
let pi = 3.14159
init(radio: Double) {
self.radio = radio
}
func calcularArea() -> Double {
return pi * pow(radio, 2)
}
}
class CalculadorDeAreas {
var figuras: [TieneArea]
init(figuras: [TieneArea]) {
self.figuras = figuras
}
func calcular() -> Double {
var area = 0.0
for figura in self.figuras {
area += figura.calcularArea()
}
return area
}
}
let calculadorDeAreas = CalculadorDeAreas(figuras: [Rectangulo(alto: 3, largo: 4), Circulo(radio: 5)])
print("El área es \(calculadorDeAreas.calcular())")

view raw
OCP.swift
hosted with ❤ by GitHub

En esta versión utilizamos un protocolo: TieneArea, que define a la función calcularArea, y las clases Rectangulo y Circulo implementan este protocolo brindando una implementación propia para el cálculo del área según el tipo de figura que las mismas representan. De este modo si queremos extender el funcionamiento de la clase CalculadorDeAreas y su función calcular, no necesitamos modificar las mismas sino que basta con crear una nueva clase que representa la nueva figura y hacerla implementar el protocolo TieneArea.

One thought on “Principio Open-Closed en Swift

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s