Home » Guías » Swift 2: extensiones de protocolos
Extensiones de Protocolos

Swift 2: extensiones de protocolos

Hablamos en su momento sobre la teoría del nuevo concepto de la programación orientada a protocolos, pero antes de verlo con ejemplos vimos una lección de Swift donde hablábamos más en profundidad sobre los structs, una de las bases imprescindibles y también sobre los propios protocolos.

Ahora toca el turno de ver el siguiente componente imprescindible: las extensiones de protocolos.

Extensiones

¿Qué es una extensión? Pues básicamente es una funcionalidad que nos permite añadir métodos y propiedades (no almacenadas, si no calculadas) a cualquier clase, estructura o enumeración del sistema (y ahora a los protocolos también). No importa que no dispongamos del código fuente de la clase, estructura, enumeración o protocolo que queramos extender o que este sea un componente del sistema como, por ejemplo, el tipo String. Las extensiones nos permiten añadir funcionalidad y que esta se integre como si siempre hubiera formado parte del tipo de estructura extendida.

Como vemos en este ejemplo, podemos tener una función posicionEn que recibe una subcadena y devuelve la posición de inicio de la misma como un número entero e incorporarla al tipo String de forma que se integre como una función más de este. Así podemos obtener funciones más cómodas, prácticas y personalizadas a nuestras necesidades. Desde el momento que creamos esta extensión, cualquier cadena que usemos, incluso una cadena pura que no esté en ninguna variable o constante, podrá llamar a esta función como si fuera un método propio que siempre hubiera existido en ella.

Partiendo de esa base, Apple ha usado este tipo de funcionalidad (las extensiones) para proporcionar una vía por la que conseguir la nueva abstracción por protocolos. Pero, ¿qué es exactamente una extensión de protocolo?

Extensiones de protocolos, funcionalidad por defecto

Una extensión de protocolo lo que permite es indicar, no solo la especificación que queramos que se incluya cuando una clase, struct o enumeración cumplan o se conformen al protocolo, también nos permite indicar la funcionalidad por defecto que vamos a querer incluir en dicho protocolo en caso que no la incluyamos nosotros. Y además esta funcionalidad por defecto será siempre opcional, de forma que cualquier estructura que se conforme al protocolo podrá usarla si lo quiere pero no necesariamente e incluso sobrescribir la propia implementación.

En una implementación convencional de un protocolo haríamos algo parecido a esto:

Creamos un protocolo Personaje que nos va a permitir usar varios tipos de estructuras, clases e incluso enumeraciones, que para cumplir con este habrán de incluir un parámetro vida de tipo entero y que disponga de las funciones para recoger su valor ( get) y establecer su valor ( set). Además, tendrán que incluir una función llamada muerte() cuyo motivo de existencia es que debemos definir cómo morirá nuestro personaje, siendo una implementación diferente para cada tipo o estructura que vaya a conformarse a este protocolo. Por lo tanto, un struct de un Guerrero podría ser algo así:

Hasta aquí, no hemos contado nada nuevo. Pero, ¿y sí queremos establecer un método de muerte del personaje por defecto?. Probablemente pensaríamos que esta aproximación no es la más apropiada y que sería mejor crear una clase Personaje que tenga un método muerte() con una implementación ya establecida y luego, al instanciar o cuando creemos una subclase, usar dicha implementación o incluso podremos sobrescribirla convenientemente.

Pero es más simple usar extensiones de protocolos, más eficiente para el sistema y lo más importante: usamos tipos de datos por valor y no por referencia, por lo que la gestión de memoria es más cómoda para el sistema. Si usamos la palabra extension como haríamos normalmente, ponemos el nombre del protocolo y trasladamos nuestra función del struct muerte() a este protocolo.

Nada más simple. De hecho, el verdadero potencial de la extensión es que si borramos la función muerte() de nuestro struct, no veremos error alguno porque seguiremos conformados al protocolo. Ahora la función, que tiene una especificación por defecto, puede o no ser incluida en aquel tipo o estructura que queramos conformar al mismo, pero siempre estará disponible para ser usada cuando instanciamos y creemos objetos o instancias de un struct con él.

Creando una instancia de un Guerrero vemos que la función muerte() existe, aunque como ya hemos visto no está incluida en el struct Guerrero si no que se está incluyendo directamente desde la especificación del protocolo. ¿Y si queremos crear nuestra propia funcionalidad? No tenemos más que incluir la función en el struct, darle otra especificación y listo. Será igual que si hubiéramos hecho un override de una función en una subclase que herede un método de su clase padre.

Y ahora ya estamos preparados para la Orientación a Protocolos de Swift 2, que veremos en un próximo artículo, y donde además veremos cómo se pueden condicionar las extensiones para que solo puedan ser usadas cuando el tipo que queremos conformar al mismo cumpla con una serie de condiciones previas.

Hasta entonces, probad, practicad lo aprendido (que está verificado con Xcode 7 Beta 6) y Good Apple Coding.

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

ARKit

Analizando ARKit

Realizamos un análisis de ARKit, sus posibilidades, cómo funciona y lo comparamos con las Microsoft Hololens para ver el alcance de sus posibilidades. Un análisis en detalle incluyendo algunas pruebas en vídeo que hemos realizado mientras preparamos el curso de ARKit en Apple Coding Academy que verá la luz en septiembre, una vez tengamos versión final de esta interesante plataforma.