¡Atención! Este sitio utiliza cookies.
Si no cambias la configuración de tu navegador, aceptas su uso.

Semillas de Cacao: El patrón de diseño del ‘Delegado’.

El protocolo del delegado es quizá uno de los conceptos que más cuesta entender para los nuevos en Cocoa. La idea de escribir métodos que no hemos declarado nosotros, no están en la superclase, que simplemente parecen funcionar automágicamente, incomoda al principio, sobre todo a aquellos reacios a los ‘saltos de fe’ que vienen de otros lenguajes.

Definición del protocolo del ‘delegate’

«La Delegación es el traspaso de una tarea a otra parte del programa. En Programación Orientada a Objetos se usa para describir una situación en la que un objeto asigna una tarea a otro, conocido como el delegado (delegate).»

En otras palabras, en ocasiones una clase realiza una tarea genérica que requiere de cierta acción específica, es decir, se divide la responsabilidad en 2 clases: una que desarrolla la tarea principal, y otra que se ha designado como delegada para acabar el trabajo si hace falta.

Si te has quedado con cara de tonto no pasa nada, solo Chuck Norris y MacGyver lo pillaron a la primera.

Foto de Macgyver "ni navaja suiza ni hostias, un delegate"

MacGyver le pilló el gusto a delegar en seguida, lo que pasa es que no lo usaba

En esta entrada voy a tratar de explicar este patrón de diseño tan poco conocido (al menos para quienes comienzan su andadura en la programación) y que tantos quebraderos de cabeza da cuando no tenemos otra que pasar por el aro, sobre todo cuando heredamos de clases tan útiles como UITableViewController. Es un concepto imprescindible para comunicar un MVC con otro y que se usa bastante una vez se le coge el gustillo.

Primera piedra. El delegado ‘no manda’.

No serás el primero ni el último al que lo primero que se le viene a la cabeza concepto de delegado de clase en el instituto: una persona de cierta importancia, con capacidad de decisión y mucha responsabilidad.

Y una mierda.

Tus recuerdos te juegan una mala pasada o quizá nunca fuiste delegado. Si te paras a pensar, el delegado de la clase era quien se comía todos los marrones que el resto de la clase y el claustro de profesores no querían ni oler. Era (o eras) un ‘pringao’ al que simplemente se le avisaba de ciertas incidencias y que tenía que hacer algo al respecto: repartir papeles, recoger firmas, asistir a reuniones y yo qué sé qué más…

En Cocoa es exactamente igual. Una clase delegada deja el trabajo duro a otra, la principal. Cuando esta última necesita un poco de ayuda, llama, ahora sí, a su delegada; es decir, se detiene y delega en ella cierta responsabilidad antes de seguir trabajando. Ninguna clase debería ser Batman.

El Sauron navarro también delega

El Sauron navarro también delega

Podríamos decir que, en cierta forma, la clase que delega es una ayudante, una simple escudera de otra que de vez en cuando le echa una mano . Ninguna manda a la otra, ni una debería ser necesariamente más importante, pero evidentemente solo una de las dos manda los mensajes. La otra solo se suscribe, escucha y actúa.

Segunda piedra. ¿Dónde manipulo las propiedades y métodos de la clase que me manda los mensajes?

No manipulas la otra clase. Nunca. Ni se te ocurra. Si la clase que está delegando en ti, llamémosla la Principal, te pide cierta información como parámetros de retorno, dáselos, pero no te entrometas en su trabajo. Ella no meterá las narices en tu clase, así que no metas tú las tuyas en la ajena. Recuerda el Principio de Responsabilidad Única. Si estoy cocinando y te pido que bajes a por pan, tráeme pan, pero después no me toques las pelotas.

En el fondo el delegado es un poco pagafantas

En el fondo el delegado es un poco pagafantas

Y quién me dice qué mensajes tengo que saber responder cuando mi clase se hace delegada de otra.

Pues la que envíe los mensajes. Todos los métodos que un delegado puede o debe implementar estarán declarados en un protocolo, que como habrás adivinado, está en la clase principal, la que los manda.

Un protocolo es algo así como un contrato o un compromiso… Le exige a una clase un cierto comportamiento para ser exactos.

No todos están capacitados

Si no estás capacitado, mejor no toques

Al igual que cuando buscas trabajo, tú aseguras tener los conocimientos necesarios para saber responder a las necesidades inmediatas de un puesto concreto. Si las cumples bien, y si no, pues a no ser que seas funcionario, te enseñan la puerta; o en este caso, una petada bien merecida, ya que como delegado ibas a recibir ciertos mensajes, y si un objeto recibe un mensaje que no implementa… Ya sabes lo que pasa. Catapún.

Y qué, ¿me tengo que meter en la cabecera de la clase correspondiente y leerme todos los métodos del protocolo? Y un mojón. He leído programas electorales más entretenidos.

Pues tampoco hace falta que te fustigues innecesariamente, ya que como te habrás pispado, en Cocoa reinan las convenciones, todas esas ‘normas’ (¿no escritas?) que seguimos para dar coherencia al código y evitar ser demasiado ‘creativos’. Pues este caso no iba a ser menos.

Todos los métodos que implementemos de otra clase comienzan siempre por el nombre esa clase.

Por ejemplo, si nos hemos suscrito a los mensajes de ‘UITableViewDelegate’ en nuestra cabecera, podremos encontrar fácilmente los métodos de esta si escribimos correctamente el valor de retorno, y tableView como inicio de firma del método, es decir, -(void) tableView:didSelectRowAtIndexPath:, por ejemplo.

Si se siguen las convenciones, encontraremos gracias al autocompletado de Xcode todas las funciones con las que podemos jugar. Aunque si quieres puedes liarte la manta a la cabeza y leerte la cabecera de todas las clases de Cocoa, aunque te aviso que planean hacer una película sobre el tema.

Si responde bien, y si no, también.

Puede que nos olvidemos que asignarnos como delegados antes de empezar a recibir mensajes, un despiste bastante habitual, pero aún así verás que no ocurre nada catastrófico. Has importado la clase en cuestión, te has suscrito a todas las historias que tiene que decirte, y, sin embargo, cuando ejecutas el código la marrana se lo calla todo, no te notifica.

Pues sí, lo has adivinado, está mandando mensajes a ‘nil’, y le da igual, por cierto. Una clase tiene que seguir su camino, estés tú o no. Si lo de que el delegado era un ‘pringao’ no te había quedado claro, aquí tienes otra razón para que te lo creas.

Recuerdo en mis días de delegado de clase (de estudiante quiero decir) mi particular indiferencia acerca del tema. Sinceramente, consideraba ese papel una broma con poca gracia de mis compañeros. Así que para demostrar que yo también tenía sentido del humor, un año decidí no responder a tales funciones. Y oye, nunca pasó nada… Todo siguió su curso. Más o menos.

En Cocoa es igual. Da igual que mandes mensajes a un objeto que está a nil. Como suelo decir, nil es el objeto Alderaan de Cocoa. Tú mándale mensajes, que no te va a contestar ni el tato.

Alderaan

Si Alderaan es ‘nil’ no pasa nada. Y bien pensado, será mejor contar con que ese pueda ser el caso…

Vale. Lo pillo. Me tengo que suscribir y además asignar como delegado manualmente para que me lleguen los mensajes. ¿Y cómo se asegura la clase Principal de que soy un tío legal?

Cualquier clase que declare un protocolo en su cabecera tendrá también una propiedad llamada delegate de tipo id, es decir, que acepta cualquier tipo de clase como ayudanta. En tiempo de ejecución, cualquier objeto podrá asignarse como delegado (eso sí, solo uno a la vez) y recibir los mensajes que se envíen a esta propiedad. En efecto, se mandaran mensajes a ese delegate, y si hay alguien al otro lado bien, y si no, pues también.

La cuestión es, ¿cómo sabe este pollo que no le están dando gato por liebre? Si manda un mensaje a nil no pasará nada, pero si no es así y lo recibe un objeto que no lo implementa —o incluso, que ni siquiera se había suscrito al protocolo (el contrato)—, parece que tendremos una visita probable al main.m

De nuevo, el delegado será el ‘pringao’ que se suscriba a los menajes de la clase que declara el protocolo, y además se tendrá que autoasignar como tal. Eso sí, solamente si se ha cumplido antes la primera condición. La propiedad delegate de la clase Principal normalmente solo aceptará a esos miserables que acepten sus condiciones (de nuevo, el protocolo), pero vaya, es por el bien común (y para que no te pongan una estrellita en la AppStore cuando le pete a algún cateto).

Gollum y el anillo

Si Gollum no entiende los mensajes de Sauron, no parece un buen candidato a delegado…

Me sigo sin enterar. Que lo sepas.

La única manera de entender estos conceptos es ponerlos en práctica y usarlos en un caso práctico. Te recomiendo comenzar por la clase UITableView y sus delegados Datasource y, Delegate propiamente. Es la mejor forma de entender el concepto de ‘escuchar y responder preguntas’.

Después, implementar tu propio protocolo te ayudará a entender el ‘otro lado’ de este mecanismo.

Recuerda, para distinguir entre un delegado y una clase principal, tendrás que hacerte una simple pregunta: ¿quién va a enviar mensajes a quién?

Michael Knight y Kitt

¿Quién es Michael Knight y quién hace de Kitt?

Un ejemplo práctico

Digamos que Sauron quiere encontrar su preciado anillo, como no podría ser de otra manera. Como no puede moverse de Barad-Dûr necesita a alguien que le eche una mano en caso de que necesite dicho anillo para dominar la Tierra Media. Durante siglos urde su plan: gobierna Mordor, sube y baja los impuestos a los orcos, controla las tasas de natalidad, o lo que sea que haga, y en un momento dado dice «vale, ya tengo todo listo, ahora necesito mi anillico». Pues bien, en ese momento Sauron se da cuenta de que no puede moverse de la torre y no tiene más remedio que delegar esa responsabilidad en otra entidad (una clase/objeto en nuestro caso). En el caso de El Señor de los Anillos, esta entidad serían los Nazgûl, los nueve reyes antiguos que se hicieron funcionarios de Mordor cuando la cosa se puso fea. Estos nueve jinetes salen en busca del anillo de Sauron con el fin de devolvérselo. Parten de Minas Morgul (o de donde sea) con el compromiso (el protocolo) de responder a la petición de Sauron.

Aquí tendríais el código más básico:

Esta sería la pinta que tendría el .h de Sauron.

Su fichero de implementación tendría esta pinta:

 

 

Esta sería la forma en la que los nazgûl en este caso, implementarían su cabecera.

Y este sería su archivo de implementación

 

¿En cristiano?

Sauron declara en su cabecera que tiene intenciones de pedir el anillo en algún momento (cabecera) y llama a su delegado (implementación) cuando lo requiere.

Por parte del delegado, este se suscribe (cabecera) al protocolo de Sauron y se asigna como delegado (en el .m)  de Sauron, implementa su método en el  -(Ring*) sauron:(Sauron) sauron ringForHobbitAtIndexPath:(NSIndexPath *) indexPath y… ¡listo!

En este punto, el delegado ha declarado que entiende los mensajes de la clase Principal (los que están en el protocolo), y ahora recibirá dichos mensajes porque se ha posicionado/asignado en la propiedad delegate (de no haberlo hecho no los recibiría por mucho protocolo que haya jurado).

Este es uno de los patrones más utilizados en Cocoa, así que más te vale entenderlo, porque te vas a hartar de verlo…

Escrito por Miguel Hernández Jaso

Autor del blog. Desarrollador especializado en iOS.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Mi Gobierno me obliga a decirle que utilizo 'cookies' en mi página. Si continúa navegando, quiere decir que está conforme con ello.