Protocolos con Tipos Asociados

Tutorial Swift – Protocolos con Tipos Asociados

Josué V. Herrera Swift Avanzado, Tutoriales 0 Comments

En este Tutorial Swift aprenderemos acerca de los protocolos con tipos asociados (associatedtype), un tema que da seguimiento al artículo sobre código genérico. Para entender cuan importante son los tipos asociados o los associated types primero necesitamos hacer una introducción, ponernos en contexto.

Cuando definimos un protocolo y tenemos que declarar la firma de un método genérico nos encontramos ante un problema. Como ya sabemos en el caso de los métodos genéricos no sabemos con antelación que tipo de dato este manejará, entonces ¿cómo especificamos esto dentro de la declaración del protocolo teniendo en cuenta de que no podemos usar T?

AssociatedType

La solución al problema anterior reside en el uso de tipos asociados como parte de la propia definición del protocolo, siendo esta la vía de hacer referencia a este valor a priori del tipo de dato final.

Mejor veamos un ejemplo de un tipo asociado en acción:

…el tipo asociado (associatedtype) de nombre  ItemType enmascara un tipo de dato que aún no se conoce y es evidente que así sea, ya que nosotros desconocemos quien implementará el protocolo y por ende el tipo de dato que usará. Dicho esto, nos damos cuenta de la similitud, ItemType funge como un comodín y esto nos recuerda el artículo sobre código genérico, nos recuerda a T.

El protocolo Container define tres requerimientos que todo aquel que lo adopte tiene que cumplir:

  • Tiene que permitir agregar un elemento nuevo al contenedor haciendo uso del método append(_:).
  • Tiene que permitir el acceso al número de elementos que forman parte del contenedor a través de la propiedad count.
  • Tiene que permitir obtener cada elemento del contenedor con un subscript que reciba como parámetro el índice del valor.

Como podemos constatar este protocolo no especifica como se almacenarán los elementos en el contenedor o de que tipo de dato estos debieran de ser, solamente se declaran las tres funcionalidades que tienen que ser implementadas para que pueda ser adoptado satisfactoriamente.

Veamos un ejemplo de este protocolo en uso:

A partir de la línea 29 vemos las características asociadas al protocolo, y de hecho también en esta misma línea igualamos ItemType al tipo de dato numérico Int. Por razones más que evidentes en este punto ya conocemos que la Pila IntStack, como su propio nombre lo indica, trabaja solamente con el tipo Int, por ende en esta línea estamos informando al compilador de manera explicita de que el tipo ItemType ya no es desconocido.

En honor a la verdad la línea 29 es completamente para fines de estudio, de claridad, en pos de transmitir mejor lo que intento explicar. Sí, digo esto último ya que aunque no está mal, no es necesaria, pues el compilador de Swift infiere a través de la implementación de la estructura IntStack que el tipo al que sustituirá ItemType será Int.

Si te estás preguntando si esto que hemos hecho se pudiera aplicar a un código genérico, pues sí, veamos este ejemplo de la versión genérica de nuestra Pila:

…la salida en pantalla sería:

Como era de esperar funciona perfectamente, y trabajamos con T como nuestro comodín y como ven no ha habido necesidad de hacer referencia a ItemType ya que como he comentado anteriormente el tipo de dato es inferido por el compilador de Swift.

Restringiendo la Extensión de Protocolos

Los protocolos también pueden ser extendidos y en estas ocasiones quizás nos resulte útil filtrar ciertos comportamientos, es decir que la extensión de código se encuentre disponible solamente bajo ciertos eventos como pudiera ser que el tipo de dato comodín sea igual a String. Veamos un ejemplo de esto:

…la salida en pantalla:

Hasta aquí todo bien, nada nuevo. Imaginemos ahora que necesita una implementación por defecto del método eat para todos aquellos que lo adopten, para lograr esto hacemos uso de las extensiones, de la siguiente forma:

…la salida en pantalla sería la siguiente:

De la línea 9 a la 17 extendemos el protocolo Animal con una implementación por defecto del método eat y donde especificamos (haciendo uso de la sentencia where), que esta extensión solamente estará disponible para aquellos usuarios donde el comodín Food sea igual al tipo de dato String.

Si seguimos analizando el código vemos que hemos omitido el método eat en la estructura Cow dejando solamente la declaración explicita de que Food es de tipo String. Proseguimos creando una instancia de Cow y hacemos uso del método eat sin problemas. Lo siguiente es otra declaración, en este caso de una estructura de nombre Cat pero a la que no le definimos el método eat y que al igual que la anterior, Food también es de tipo String.

Los tipos asociados son de gran utilidad cuando trabajamos con protocolos y queremos definir funciones o método genéricos. Pero también tenemos que ser cuidadosos, como podemos ver en la salida en pantalla del último código, el descuido que hemos cometido nos han creado un gato con problemas de identidad, el gato se cree una vaca y aun así come pescado :-).

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

Cualquier sugerencia, ya sea errores a corregir, información o ejemplos a añadir será, más que bienvenida, necesaria!