SOLID: Principio de abierto/cerrado u Open/closed principle

Como vimos en el artículo de los principios SOLID este es el segundo principio.

Significado

Este principio Establece que las clases deben estar abiertas para su extensión, pero cerradas para su modificación.

Ejemplo

Siguiendo con el ejemplo de la clase Persona que vimos en el artículo del principio de responsabilidad única imaginemos que tenemos una lista de personas(array) y queremos imprimir por pantalla las personas que tengan la edad suficiente para conducir un vehículo. Pero debemos tener en cuenta que según el país esta edad puede ser distinta. Para completar este programa crearemos una clase ListaDePersonasMayoresDeEdad que resolverá este problema con la función imprimeConductores().

Para el ejemplo de código en lugar de cargar los datos de personas de la base de datos tendremos una lista que se rellenará en el constructor de la clase ListaDePersonasMayoresDeEdad.

El código de ejemplo quedaría así:

 

class Persona {
    propiedad nombre
    propiedad apellidos
    propiedad nacionalidad

    propiedad fechaDeNacimiento

funcion calculaEdad()
}

 

class ListaDePersonasMayoresDeEdad {

    propiedad listaDePersonas

 

    funcion Constructor() {

        listaDePersonas = [

            Persona(“Fulano”, “De tal”, Español, 1/5/1981),

            Persona(“Mengano”, “De cual”, Mejicano, 3/12/1992),

            Persona(“John”, “Doe”, Norteamericano, 13/2/2012),

            Persona(“Boris”, “Stalin”, Ruso, 20/9/1999)

        ]

    }

 

    funcion imprimeConductores() {

        bucle personaDeLaLista en listaDePersonas {

            Si personaDeLaLista.nacionalidad = Español Y personaDeLaLista.calculaEdad() >= 18

            entonces imprime(personaDeLaLista)

            Si personaDeLaLista.nacionalidad = Norteamericano Y personaDeLaLista.calculaEdad() >= 16

            entonces imprime(personaDeLaLista)

            Si personaDeLaLista.nacionalidad = Ruso Y personaDeLaLista.calculaEdad() >= 18

            entonces imprime(personaDeLaLista)

        }

    }

}

 

Este programa no cumple el principio de abierto/cerrado por la sencilla razón de la necesidad de modificar la clase ListaDePersonasMayoresDeEdad cada vez que haya una nacionalidad nueva en nuestra base de datos. La función imprimeConductores() tendría que ser modificada cada vez que en la base de datos apareciese un usuario con una nueva nacionalidad.

Solución

Dependiendo de las características y las capacidades del lenguaje de programación que estemos utilizando la solución puede crearse mediante clases abstractas, protocolos o empleando una solución generalista como la creación de una clase nacionalidad que encapsule ciertos requisitos en la base de datos.

El objetivo de la solución se enfoca en evitar tener que modificar una función o clase en el futuro simplemente porque se agreguen nuevos datos a la base de datos.

Recordemos que este principio nos mueve a extender una clase para evitar estar modificando otra constantemente. Por esa razón podemos extender la clase Persona gracias a la nueva clase Nacionalidad

Veamos el código de esta solución.

 

class Persona {
    propiedad nombre
    propiedad apellidos
    propiedad nacionalidad

    propiedad fechaDeNacimiento

funcion calculaEdad()
}

 

class Nacionalidad {

    propiedad nombreDeNacionalidad

    propiedad edadParaConducir

}

 

class ListaDePersonasMayoresDeEdad {

    propiedad listaDePersonas

    propiedad nacionalidades 

    funcion Constructor() {

        nacionalidades = [

            Nacionalidad(Español , 18),

            Nacionalidad(Norteamericano , 21),

            Nacionalidad(Mejicano , 15),

            Nacionalidad(Ruso , 18),

        ]

 

        listaDePersonas = [

            Persona(“Fulano”, “De tal”, Español, 1/5/1981),

            Persona(“Mengano”, “De cual”, Mejicano, 3/12/1992),

            Persona(“John”, “Doe”, Norteamericano, 13/2/2012),

            Persona(“Boris”, “Stalin”, Ruso, 20/9/1999)

        ]

    }

 

    funcion imprimeConductores() {

        bucle personaDeLaLista en listaDePersonas {

            Si personaDeLaLista.calculaEdad() >= personaDeLaLista.nacionalidad.edadParaConducir

            entonces imprime(personaDeLaLista)

        }

    }

 

}

 

Ahora el bucle que hay en la función imprimeConductores() se ha reducido y es más legible.

Esta solución es más fácil de mantener y facilita que la clase Nacionalidad pueda incorporar más información interesante para nuestra aplicación y su mantenimiento ha mejorado al no existir la necesidad de modificar eternamente una función de una clase concreta dependiendo de qué datos hay en la base de datos.

SOLID: Principio de responsabilidad única o Single responsibility principle

Como vimos en el artículo de los principios SOLID este es el primero de los principio SOLID.

Significado

Este principio establece que una clase o componente debe ser responsable de una sola cosa.

Este principio está alineado con el término de desacoplamiento o decoupled utilizado en muchas metodologías de desarrollo.

Cuando se codifica una clase que sólo posee una responsabilidad o función es fácil diseñar tests para comprobar que funciona correctamente y además facilita que esa clase pueda ser reutilizada en futuros proyectos.

Ejemplo

Imaginemos el siguiente ejemplo en el que tenemos una clase Persona que contiene los datos de un usuario.

El código de nuestra clase podría ser algo como:

 

class Persona {
    propiedad nombre
    propiedad apellidos
    propiedad nacionalidad
    propiedad fechaDeNacimiento

funcion calculaEdad()
    funcion guardaEnBaseDeDatos()
    funcion cargaDesdeBaseDeDatos()
}

Este ejemplo viola el principio de responsabilidad única.

Esto se debe a que la clase Persona además de guardar la información de una persona también se encarga de almacenar y recuperar la información de una base de datos.

Imaginemos que nuestra aplicación está utilizando MySQL pero en una futura versión queremos incluir soporte para la base de datos RealM manteniendo la opción de MySQL. Los cambios que tendríamos que hacer a nuestra clase persona complicarían enormemente el código.

En cambio si creamos una clase Persona que almacene los datos de un usuario y otra clase PersonaMySQL que se encargue de recuperar y almacenar los datos en nuestra base de datos MySQL permitiría fácilmente la codificación de una nueva clase PersonaRealM que haga lo mismo que la clase PersonaMySQL pero adaptando el código a la base de datos RealM.

 

class Persona {
    propiedad nombre
    propiedad apellidos
    propiedad nacionalidad
    propiedad fechaDeNacimiento

funcion calculaEdad()
}

class PersonaMySQL {
    funcion guardaEnBaseDeDatos()
    funcion cargaDesdeBaseDeDatos()
}

class PersonaRealM {
    funcion guardaEnBaseDeDatos()
    funcion cargaDesdeBaseDeDatos()
}

Nuestro programa será mucho más cohesivo y estará más encapsulado aplicando este principio. Siguiendo esta forma de agrupar funciones y propiedades en nuestras clases se simplifica las operaciones de mantenimiento como localizar dónde hay un error o ampliar la funcionalidad de nuestra aplicación agregando soporte a otras bases de datos.

Principios de programación SOLID

En el diseño y desarrollo de software el paradigma de la programación orientada a objetos (POO) es el paradigma más utilizado a la hora de desarrollar software.

En la programación orientada a objetos podemos dividir un programa en muchas entidades con propiedades y funciones que se relacionan entre ellas.

La persona encargada de codificar cada una de estas clases y relaciones tiene total libertad a la hora de definir quién, cómo y dónde se resuelve cada mini problema.

Esta libertad de codificación facilita la resolución de los problemas que debe resolver una aplicación pero cuando el proyecto es grande la complejidad en el mantenimiento y ampliación de una aplicación crece notablemente haciendo muy dificultoso utilizar programación orientada a objetos de forma libre en proyectos de gran envergadura. 

Esto se debe a que las clases encargadas de un problema se vuelve demasiado grandes y las relaciones y dependencias entre clases cada vez son más complejas.

Principios para mantener el orden en nuestro código

A principios de la década de los 2000 Robert C. Martin propuso una metodología de codificación basada en 5 principios.

Tomando la primera letra de cada uno de esos principios sale el nombre de esta metodología que conocemos por principios SOLID.

Estos principios son:

  • S: Single responsibility principle o Principio de responsabilidad única
  • O: Open/closed principle o Principio de abierto/cerrado
  • L: Liskov substitution principle o Principio de sustitución de Liskov
  • I: Interface segregation principle o Principio de segregación de la interfaz
  • D: Dependency inversion principle o Principio de inversión de dependencia

Razones para utilizar SOLID

Aplicando estos principios a la hora de codificar un proyecto software mejorará su legibilidad y su mantenimiento tanto por desarrolladores individuales como por equipos de desarrollo.

En la industria actual del software el trabajo en equipo es algo imprescindible para un buen profesional y saber codificar un proyecto para que tenga un buen nivel de mantenimiento para incorporar nuevas características o localizar y resolver problemas en el código es una habilidad muy solicitada por los equipos de desarrollo de software. 

Veremos cada uno de estos principios en futuros artículos publicados en este blog.

Convierte de texto a voz con AppleScript

A veces puede que necesitemos convertir un fichero de texto a un fichero de audio utilizando las voces que vienen incluidas en MacOS. Para este tipo de situaciones Tyflos accessible software ha publicado un proyecto sencillo que realiza esta función.

Este proyecto incluye un script de AppleScript que te permite seleccionar una de las voces del sistema y abrir un fichero de texto en formato txt. El script realizará la conversión a fichero de audio con la voz seleccionada de forma automática.

Este proyecto es de caracter software libre por lo que cualquiera puede utilizarlo y participar para mejorarlo.

Puedes encontrar el script en la página del proyecto TTSConverter en Github.

Crea un script para contar tiempo en AppleScript

A veces puede que necesitemos contar cuánto tiempo ha transcurrido desde un evento a otro mientras utilizamos nuestro equipo con MacOS. Con AppleScript podemos crear un script que realice esta cuenta de tiempo.
Con lo que conocemos de AppleScript y lo visto en los artículos de Crea un cronómetro de cuenta atrás en AppleScript, Controlar variables no definidas en AppleScript y Obtener la hora actual en AppleScript de distintas formas podemos crear el siguiente script:

global timeValue, counting

on getTimeInSeconds()
	-- set t to (time of (current date))
	return (time of (current date)) as number
end getTimeInSeconds

on startCount()
	set counting to true
	set timeValue to getTimeInSeconds()
	say ("Contando")
end startCount

on stopCount()
	set numberOfSeconds to getTimeInSeconds() - timeValue as integer
	set numberOfHours to numberOfSeconds / 3600 as integer
	if numberOfHours > 0 then
		set numberOfSeconds to numberOfSeconds - (numberOfHours * 3600) as integer
	end if
	set numberOfMinutes to numberOfSeconds / 60 as integer
	if numberOfMinutes > 0 then
		set numberOfSeconds to numberOfSeconds - (numberOfMinutes * 60) as integer
	end if
	say ("Tiempo transcurrido " & numberOfHours & " horas, " & numberOfMinutes & " minutos y " & numberOfSeconds & " segundos.")
	set timeValue to 0
	set counting to false
end stopCount

on run
	try
		if counting = false then
			startCount
		else
			stopCount()
		end if
	on error number -2753
		startCount()
	end try
end run

Obtener la hora actual en AppleScript de distintas formas

En AppleScript podemos obtener la hora actual de diversas formas según qué necesitemos hacer.
Podemos obtener la hora actual en horas, minutos y segundos de la siguiente forma:

set tiempo to (time string of (current date))
display dialog tiempo

Pero podemos obtener el número de segundos transcurridos desde la madrugada hasta ahora:

set tiempoEnSegundos to (time of (current date)) as number
display dialog tiempoEnSegundos

Con cada una de las formas podemos realizar distintas funcionalidades y herramientas.