Colaboración con la asociación AMIRES

La asociación AMIRES está realizando un proyecto de divulgación de experiencias de personas con discapacidad visual que comparten sus conocimientos y soluciones sobre diversos temas que afectan a la vida profesional y personal de personas con discapacidad.

Recientemente me invitaron a colaborar participando en una serie de entrevista para hablar de diversos temas.

Esta primera entrevista en la que se tratan temas de recursos de accesibilidad y programación ya se puede visualizar en Youtube.

Nueva participación en el podcast Unicode(U+00D1) para seguir hablando de accesibilidad

Una nueva participación en el podcast Unicode(U+00D1) para seguir hablando de accesibilidad.

Aunque ya he hablado de accesibilidad en ste podcast para desarrolladores en el que se tratan temas muy diversos

En este nuevo episodio hablo sobre cómo hacer tu proyecto accesible y no morir en el intento.

Hablé con Diego y Jorge de la problemática de diseñar un proyecto desde cero de forma accesible y de otros problemas que aparecen cuando el proyecto ya está creado y hay que incluir parches de forma más inteligente para que esos parches termien convirtiéndose en una base para el proyecto y un cimiento de accesibilidad para la evolución del producto.

Podéis escuchar el podcast en la página del capítulo.

Cómo usar la Terminal de MacOS con VoiceOver

El uso de la Terminal en MacOS, aunque no es de uso habitual para todos los usuarios, se hace indispensable para completar ciertas tareas de mantenimiento.

Esta aplicación muestra una interfaz de sólo texto en la que podemos introducir una serie de comandos y en la pantalla de la Terminal aparecerán los distintos resultados de ejecutar los comandos.

Para usar la Terminal es necesario conocer los distintos comandos que soporta pero, si la persona encargada de utilizarla además necesita VoiceOver la tarea puede complicarse un poco más.

Leer la Terminal

Una vez abierta la terminal podemos leer el texto en pantalla utilizando los comandos VO+flecha arriba y VO+flecha abajo.

Si VoiceOver no se desplaza por las líneas que muestra la Terminal debemos interactuar con la ventana de scroll de la Terminal pulsando VO+Mayúsculas+flecha abajo

Repetir los últimos comandos

Podemos volver a escribir los últimos comandos que hemos introducido en la Terminal. Para ello simplemente debemos pulsar la tecla flecha arriba y el cursor del prompt se colocará al final del último comando introducido.

Si volvemos a pulsar flecha arriba nos moveremos por el historial de comandos que hemos ido introduciendo en la Terminal.

Borrar la pantalla

Después de cierto tiempo puede que queramos borrar la pantalla de la Terminal para trabajar de forma más cómoda.

Aunque en teoría se puede utilizar el comando clear para borrar la pantalla, VoiceOver consulta el texto en pantalla de otra forma por lo que la información borrada aún puede ser leída con VoiceOver.

Para borrar de verdad la pantalla podemos ir al menú edición y buscar la opción Borrar hasta principio o pulsar el atajo de teclado Mayúscula+k

Cerrar la sesión

Podemos cerrar nuestra sesión en la Terminal simplemente cerrando la Terminal pulsando Comando+q.

Virtualiza cualquier sistema operativo en tu Mac gracias a UTM

La virtualización de un sistema operativo sobre otro es una posibilidad para dar la oportunidad al usuario a utilizar lo más apropiado en cada momento permitiendo a una persona poder usar Safari en MacOS mientras edita un documento en Microsoft Word en Windows 10 en una misma máquina. Hemos hablado de estas posibilidades en el artículo de convivencia de Windows y MacOS en hardware de apple.

Aunque en MacOS ya existían soluciones para virtualizar sistemas operativos sobre MacOS con la llegada de los nuevos procesadores Apple M1 estas soluciones dejaron de ser eficientes o totalmente incompatibles.

Tanto VMWare fusion como el proyecto VirtualBox sólo pueden ejecutarse, por ahora, en procesadores X86 y X64 por lo que no puede funcionar en procesadores Apple M1. La solución de Parallels ofrece una solución compatible con procesadores Apple M1 pero su falta de accesibilidad lo hacen incompatible con los usuarios con discapacidad.

Fue la propia Apple quien ofreció una solución para poder ejecutar de forma virtual sistemas operativos de tipo ARM64 sobre sus nuevos procesadores. Esta tecnología se conoce como Hypervisor Virtualization Framework.

El Open source al rescate

Gracias a esta tecnología de Hypervisor Virtualization Framework la comunidad de desarrolladores de software para MacOS pudo crear el proyecto UTM.

UTM es una solución software para ejecutar distintos sistemas operativos sobre hardware Mac. Aunque oficialmente puede trabajar con sistemas operativos ARM64 también se pueden instalar versiones para procesadores PPC, ARM32, X86 y X64.

Apoyo al proyecto

Aunque UTM se puede descargar gratuitamente de la página del proyecto UTM también podemos descargar UTM de la MacAppStore por un precio de 10 euros.

Descargar la aplicación de la MacAppStore nos da la tranquilidad de tener todas las actualizaciones de forma automática y cómoda sin necesidad de volver a instalar nada.

Accesibilidad de UTM

A diferencia del proyecto VirtualBox y Parallels la interfaz gráfica de usuario que presenta UTM es nativa de MacOS y resulta totalmente accesible con VoiceOver y otros productos de apoyo.

Instalación de un sistema operativo

Con UTM es sencillo crear una nueva máquina virtual. Simplemente utilizamos la opción de crear una nueva máquina virtual, indicamos el tipo de sistema operativo, asignamos memoria RAM, número de núcleos del procesador a virtualizar, espacio en disco duro y, una vez creada, podemos configurar una unidad de CD/DVD virtual que apunte a un fichero en formato ISO o crear una carpeta compartida entre el sistema operativo anfitrión y el virtualizado para poder compartir ficheros.

Tras esta configuración procedemos a ejecutar la máquina para instalar el sistema operativo utilizando tanto video con una resolución suficiente como acceso a la tarjeta de sonido.

Drivers para el hardware gracias también a UTM

El propio proyecto UTM nos proporciona la posibilidad de descargar un paquete de instalación de drivers para el hardware virtualizado por UTM para tener un correcto acceso a servicios de red, multimedia, carpetas compartidas y puertos USB.

Este paquete se conoce como Spice guest tools and Qemu drivers y se pueden encontrar en la página de soporte de UTM.

Galería de instalaciones para hacerlo aún más fácil

UTM nos ofrece una galería de máquinas predefinidas para Linux y Windows donde se nos ofrecen descargas e instrucciones para completar con éxito la instalación de una de estas máquinas.

Accesibilidad en la instalación

En cuanto a la accesibilidad del proceso con las funciones de lectura de imágenes de VoiceOver podemos llegar a leer las pantallas de arranque aunque el momento más crítico es cuando el arranque de la máquina nos pide que pulsemos una tecla para arrancar desde la unidad CD/DVD que es donde se aloja virtualmente el fichero ISO con el instalador del sistema operativo.

El resto de la interfaz de UTM es totalmente compatible con la accesibilidad de MacOS.

SOLID: Principio de inversión de dependencias o Dependency inversion principle

Este es el quinto y último principio de programación SOLID.

Significado

Este principio establece que las clases de alto nivel no deberían depender de las clases de bajo nivel. En su lugar las clases deberían depender de las abstracciones.

Además las abstracciones no deben depender de los detalles sino que los detalles deben depender de las abstracciones.

Con este principio se busca reducir las dependencias entre módulos buscando un menor nivel de acoplamiento entre clases.

Esto es indispensable en proyectos con muchos módulos en los que es necesario utilizar inyección de dependencias.

Ejemplo

Veamos el siguiente ejemplo en el que en nuestro proyecto tenemos una clase que nos permite el acceso a la base de datos en general que llamaremos CargadorDesdeBaseDeDatos y tenemos una clase PersonaEnBaseDeDatos que nos permite cargar datos de la clase Persona.

Para ello la clase PersonaEnBaseDeDatos tiene una propiedad de tipo CargadorDesdeBaseDeDatos que se utiliza como driver de acceso al sistema de almacenamiento de bases de datos de la aplicación.

 

class CargadorDesdeBaseDeDatos {

    funcion obtenerDatosGenerales()

}

 

class PersonaEnBaseDeDatos {

    propiedad datos = CargadorDesdeBaseDeDatos()

    propiedad listaDePersonas

 

    funcion cargaDatos() {

        listaGeneral = datos.obtenerDatosGenerales()

        listaDePersonas = convertirDatosGeneralesEnDatosDePersonas(listaGeneral)

    }

}

 

En el ejemplo en la clase PersonaEnBaseDeDatos dentro de la función cargaDatos() hay una dependencia de la clase AccesoABaseDeDatos. Esto es un problema porque si un día necesitamos cambiar de sistema de base de datos o de obtener datos de Internet tendríamos que modificar demasiado código ya que ambas clases tienen una fuerte dependencia entre ellas.

Imaginemos por ejemplo que queremos incluir la opción de descargar los datos desde una API rest en Internet. El código se complicaría demasiado en todas las clases.

Solución

La solución a este problema es utilizar una abstracción que permita identificar un acceso a datos mediante una interface o protocolo, dependiendo de las posibilidades del lenguaje de programación.

Veamos el código.

 

interface AccesoADatos {

    funcion cargaDatosDePersonas

}

 

class CargadorDesdeBaseDeDatos Implementa AccesoADatos {

    propiedad listaDeDatos

 

    funcion obtenerDatosGeneralesDesdeMySQL() {

        listaDeDatos = cargaDesdeMySQL()

    }

 

    funcion cargaDatosDePersonas() {

        obtenerDatosGeneralesDesdeMySQL()

        return convertirATipoPersona(listaDeDatos)

    }

}

 

class CargadorDesdeInternet Implementa AccesoADatos {

    propiedad listaDeDatos

 

    funcion obtenerDatosGeneralesDeInternet() {

        listaDeDatos = descargaDeLaNube()

    }

 

    funcion cargaDatosDePersonas() {

        obtenerDatosGeneralesDeInternet()

        return convertirATipoPersona(listaDeDatos)

    }

}

 

class PersonaEnBaseDeDatos {

    propiedad datos = AccesoADatos

    propiedad listaDePersonas

 

    funcion constructor(inyeccion: AccesoADatos) {

        datos = inyeccion

    }

 

    funcion cargaDatos() {

        listaDePersonas = datos.cargaDatosDePersonas()

    }

}

 

Con esta solución podemos inyectar cualquier clase del tipo interface AccesoADatos en el constructor de la clase PersonaEnBaseDeDatos. De esta forma podemos cambiar cómo accedemos a esos datos inyectando cualquier clase que implemente la interfaz AccesoADatos consiguiendo un mayor nivel de desacoplamiento entre clases. De esta forma tanto el módulo de alto nivel como el de bajo nivel dependen de abstracciones, por lo que se cumple el principio de inversión de dependencias y además, esto nos obliga a cumplir el principio de Liskov, ya que las clases que accederán a cualquier base de datos son intercambiables entre ellas ya que todas implementan la interfaz de AccesoADatos.

SOLID: Principio de segregación de interfaz o Interface segregation principle

En este artículo hablamos del cuarto principio de los principios de programación SOLID.

Significado

Este principio establece que las clases clientes de otras no deberían verse forzados a depender de interfaces que no usan. En su lugar se apoya la definición de interfaces más específicas para cada caso.

Cuando hablamos de interfaces estamos hablando del concepto de interfaz de clase del lenguaje de programación Java así como la adaptación a cualquier otro tipo de lenguaje de programación. Por ejemplo en Swift estaríamos hablando de protocolos.

En pocas palabras una interface es un acuerdo entre las clases que implementan la interfaz y la propia interfaz definida con sus propiedades y funciones. El acuerdo consiste en que cada clase que implemente dicha interfaz deberá codificar cada una de las propiedades y funciones definidas en la interfaz.

Ejemplo

Este principio es más sencillo de entender con un ejemplo.

Imaginemos que estamos modelando una interfaz de acciones para distintas clases de aves.

 

interface Ave {

    funcion comer()

    funcion cantar()

    funcion volar()

}

 

class Loro implements Ave {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion cantar() {

        // código para cantar

    }

 

    funcion volar() {

        // código para volar

    }

}

 

class Aguila implements Ave {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion cantar() {

        noHacerNada()

    }

 

    funcion volar() {

        // código para volar

    }

}

 

class Gallina implements Ave {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion cantar() {

        // código para cantar

    }

 

    funcion volar() {

        noHacerNada()

    }

}

 

En este ejemplo tenemos una interfaz llamada Ave con 3 acciones pero no todas las aves realizan esas tres acciones. Por ejemplo las águilas no cantan y las gallinas no vuelan. Pero si una clase implementa una interfaz está obligada a incluir esas funciones aunque no hagan nada.

Solución

La solución consiste en segregar la interfaz Ave en 3 interfaces más específicas.

 

interface Ave {

    funcion comer()

}

 

interface AveCantora {

    funcion cantar()

} 

 

interface AveVoladora {

    funcion volar()

}

 

class Loro implements Ave, AveCantora, AveVoladora {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion cantar() {

        // código para cantar

    }

 

    funcion volar() {

        // código para volar

    }

}

 

class Aguila implements Ave, AveVoladora {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion volar() {

        // código para volar

    }

}

 

class Gallina implements Ave, AveCantora {

    propiedad nombre

    propiedad color

    propiedad tamaño

 

    funcion comer() {

        // código para comer

    }

 

    funcion cantar() {

        // código para cantar

    }

}

 

Ahora cada clase sólo implementa las interfaces necesarias a sus capacidades por lo que no hay funciones que no hacen nada.

Con esta solución es sencillo incluir en un futuro por ejemplo aves que puedan nadar y cada clase sólo implementará las funciones necesarias.

Además con esta solución se mejora aún más el principio de responsabilidad única.