Birthday Sale! Up to 40% off unlimited courses & creative assets Birthday Sale! Save up to 40%!
Advertisement
  1. Code
  2. Mobile Development
Code

Padrões de Projeto: Delegação

by
Length:MediumLanguages:

Portuguese (Português) translation by David Batista (you can also view the original English article)

O padrão de delegação esta entre os padrões mais comuns de desenvolvimento em iOS e OS X. É um padrão simples que é fortemente usada pelos frameworks da Apple e mesmo o mais simples aplicativo iOS aproveita da delegação para funcionar. Vamos começar olhando à definição da delegação.

1. O que é Delegação?

Definição

A definição do padrão de delegação é pequeno e simples. Isto é como a Apple define o padrão.

Um delegado é um objeto que age em nome de, ou em coordenação com, outro objeto quando este objeto encontra um evento em um programa.

Vamos dividir isso abaixo. O padrão de delegação envolve dois objetos, o objeto delegado e o delegante. A classe UITableView, por exemplo, define uma propriedade delegate para que ela delegue eventos. A propriedade delegate precisa estar em conformidade com o protocolo UITableViewDelegate, que é definido no arquivo header da classe UITableView.

Neste exemplo, a instância da table view é o objeto delegante. O delegado é geralmente uma view controller, mas pode ser qualquer objeto que esteja em conformidade com o protocolo UITableViewDelegate. Se você não esta familiarizado com protocolos, uma classe estará em conformidade se implementar os métodos exigidos do protocolo. Vamos olhar um exemplo um pouco mais tarde.

Quando o usuário toca em uma linha na table view, o table view notifica seu delegado enviando uma mensagem de tableView(_:didSelectRowAtIndexPath:). O primeiro argumento do método é a table view enviando a mensagem. O segundo argumento é o índice da linha que o usuário tocou.

A table view apenas notifica seu delegado neste evento. Cabe ao delegado decidir o que precisa acontecer quando um evento ocorrer. Esta separação de responsabilidades, como você aprenderá em instantes, é um dos principais benefícios do padrão de delegação.

Vantagens

Reusabilidade

Delegação tem diversas vantagens, a primeira sendo a reusabilidade. Como a table view delega a interação do usuário para seu delegado, a table view não precisa saber o que precisa ser feito quando uma linha for tocada.

Em outras palavras, a table view pode permanecer ignorante sobre a implementação dos detalhes de como a interação do usuário é tratada pelo aplicativo. Esta responsabilidade é passada ao delegado, uma view controller por exemplo.

Um benefício direto é que a classe UITableView pode ser usada em muitas situações. A maior parte do tempo, não há necessidade para a subclasse UITableView adapta-lo para a necessidade do seu aplicativo.

Baixo Acoplamento

Outra vantagem importante da delegação é o baixo acoplamento. Em meu artigo sobre singletons, eu enfatizei que o alto acoplamento deve ser evitado o máximo possível. Delegação é um padrão de projeto que ativamente promove o baixo acoplamento. O que quero dizer com isso?

A classe UITableView é acoplada ao seu delegado para fazer o trabalho. Se nenhum delegado for associado com a table view, a table view não manuseará ou responderá a interação do usuário. Isto significa que é preciso haver um certo nível de acoplamento. A table view e seu delegado, no entanto, são de baixo acoplamento, porque cada classe que implementa o protocolo UITableViewDelegate pode atuar como delegado da table view. O resultado é um objeto gráfico flexível e de baixo acoplamento.

Separação de Responsabilidades

Uma vantagem menos conhecida da delação é a separação de responsabilidades. Sempre que você criar um objeto gráfico, é importante saber quais objetos são responsáveis por quais tarefas. O padrão de delegação deixa isto muito claro.

No caso da classe UITableView, o delegado da table view é responsável por manipular a interação do usuário. A table view é responsável por detectar a interação do usuário. Está é uma clara separação de responsabilidade. Tal separação torna o seu trabalho como desenvolvedor muito mais fácil e claro.

2. Exemplo

Existem alguns detalhes no padrão de delegação. Vamos continuar explorando mais o protocolo UITableViewDelegate.

Delegação

O protocolo UITableViewDelegate precisa ser implementado pelo delegado da table view. A table view notifica seu delegado sobre a interação do usuário através do protocolo UITableViewDelegate, mas ela também usa o delegado para seu layout.

Uma diferença importante entre o Swift e o Objective-C é a possibilidade de marcar métodos de protocolo como opcionais. Em Objective-C, os métodos de um protocolo são obrigatórios por padrão. Os métodos do protocolo  UITableViewDelegate, entretanto, são opcionais. Em outras palavras, é possível uma classe estar em conformidade com o protocolo UITableViewDelegate sem a implementação de todos os métodos do protocolo.

Em Swift, entretanto, para uma classe estar em conformidade à um protocolo em particular é obrigatório implementar todos os métodos definidos pelo protocolo. Isto é muito seguro uma vez que o objeto delegante não precisa verificar se o delegado implementa um método do protocolo. Esta sutil, mas importante, diferença será ilustrada adiante neste tutorial quando implementarmos o padrão de delegação.

Data Source

Este é outro padrão que é claramente relacionado com o padrão de delegação, o padrão data source. O protocolo UITableViewDataSource é um exemplo deste padrão. A classe UITableView expõe uma propriedade dataSource que é do tipo UITableViewDataSource (id<UITableViewDataSource> em Objective-C). Isso significa que a fonte de dados da table view pode ser qualquer objeto que implemente o protocolo UITableViewDataSource.

O objeto data source é responsável por gerenciar a fonte de dados do objeto que é o data source. É importante notar que o objeto data source é responsável por manter uma referência do item que ele expões ao objeto destino, tal como uma table view ou um collection view.

Uma table view, por exemplo, pede à seu data source por um dado que precisa exibir. A table view não é responsável por manter um domínio do objeto de dados que precisa exibir.  Esse papel é entregue para o objeto data source.

O padrão data source se encaixa bem no padrão Model-View-Controller ou MVC. Por que? Uma table view, por exemplo, faz parte da camada view. Ela não sabe e não deveria saber sobre a camada model e não é responsável por lidar com os dados que está chegando da camada model. Isso implica que a base de dados de uma table view, ou qualquer outra view que implemente o padrão data source, é frequentemente um controlador do algum tipo. Em iOS, normalmente é uma subclasse UIViewController.

As assinaturas dos métodos de um protocolo data source segue o mesmo padrão como aqueles de um protocolo delegate. O objeto que está enviando a mensagem para o data source é passado como primeiro argumento. O protocolo data source só deve definir métodos que referem-se aos dados que está sendo utilizados pelo objeto requerente.

Uma table view, por exemplo, pede ao data source pelo número de seções e linhas que deve exibir. Mas ela também informa ao data source que uma linha ou seção foi inserida ou deletada. O último é importante, ja que o data source precisa atualizar-se para refletir as alterações visíveis na table view. Se a table view e o data source estiverem fora de sincronia, coisas ruins acontecem.

3. Implementação

Objective-C

Implementar o padrão de delegado é bem simples, agora que sabemos como funciona. Dê uma olhada a seguir um exemplo em Objective-C.

Declaramos uma classe, AddItemViewController, que herda da UIViewController. A classe declara uma propriedade, delegate, do tipo id<AddItemViewControllerDelegate>. Note que a propriedade está marcada como weak (fraca), que indica que uma instância AddItemViewController mantem uma fraca referência do delegado.

Observe também que adicionei uma declaração do protocolo, após a instrução de importação do framework UIKit. Isto é necessário para evitar um aviso do compilador. Nós poderíamos mudar a declaração do protocolo para antes da instrução de importação, mas prefiro colocá-lo antes interface da classe. Isso não é nada mais do que uma preferência pessoal.

A declaração do protocolo também é muito simples. O protocolo AddItemViewControllerDelegate herda o protocolo do NSObject. Isto não é obrigatório, mas isso irá provar ser muito útil. Vamos descobrir por que isso um pouco mais tarde.

O protocolo AddItemViewControllerDelegate declara dois métodos obrigatórios e um método opcional. Como eu mencionei anteriormente, é uma boa prática passar o objeto de delegação como primeiro parâmetro de todos os métodos para informar qual objeto está enviando a mensagem.

Os métodos obrigatórios notificam ao delegado sobre um evento, um cancelamento ou uma adição. O método opcional pede ao delegado por um feedback. Isso espera que o delegado retorne Yes or No.

Isto é a primeira peça do quebra cabeça da delegação. Temos que declarar uma classe que declare uma propriedade delegate e temos que declarar um protocolo delegado. A segunda peça do quebra cabeça é chamar os métodos delegados na classe AddItemViewController. Vamos ver como isso funciona.

Na implementation da classe AddItemViewController, implementaremos uma action cancel:. Essa action deve ser ligada a um botão na interface do usuário. Se o usuário tocar o botão, o delegado é notificado deste evento e, como resultado, o delegado pode dispersar a instância AddItemViewController.

É recomendado verificar se o objeto delegado não é igual a nil e se ele implementa o método delegado que vamos chamar, viewControllerDidCancel:. Isso é fácil graças ao método respondsToSelector:, declarado no protocolo NSObject. Essa é a razão pela qual o protocolo AddItemViewControllerDelegate herda o protocolo NSObject. Herdando o protocolo de NSObject, temos essa funcionalidade gratuitamente.

Você pode omitir a verificação se a propriedade delegate é igual a nil, já que respondsToSelector: irá retornar nil se a propriedade delegate for nil. Eu geralmente adiciono está verificação ja que ela exibe claramente o que estamos testando.

A terceira e ultima peça do quebra cabeça é a implementação do protocolo delegado pelo objeto delegado. O código a seguir demonstra a criação de uma instância AddItemViewController e a implementação de um dos métodos delegados.

Não se esqueça de garantir a conformidade da classe que atua como delegado com o protocolo de AddItemViewControllerDelegate, como mostrado abaixo. Você pode adicionar na interface da classe ou em uma extensão de classe privada.

Swift

Em Swift, o padrão de delegação é igualmente fácil de implementar e você irá notar que o Swift torna a delegação ligeiramente mais elegante. Vamos implementar o exemplo acima em Swift. Assim é como a classe AddItemViewController fica em Swift.

A declaração do protocolo é um pouco diferente em Swift. Note que o protocolo AddItemViewControllerDelegate herda o NSObjectProtocol ao invés do protocolo NSObject. Em Swift, classes e protocolos não podem ter o mesmo nome, é por isso que o protocolo NSObject tem um nome diferente em Swift. 

A propriedade delegate é uma variável do tipo AddItemViewControllerDelegate?. Note o ponto de interrogação no final do nome do protocolo. A propriedade delegate é um opcional.

No método cancel(_:), chamamos o método delegado viewControllerDidCancel(_:). Esta única linha demonstra como o Swift pode ser elegante. Podemos desempacotar a  propriedade delegate por segurança antes de chamar o método delegado. Não é necessário verificar se o delegado implementa o método viewControllerDidCancel(_:) já que todos os métodos do protocolo são obrigatórios em Swift.

Agora vamos olhar a classe ViewController, que implementa o protocolo AddItemViewControllerDelegate. A interface demonstra-nos que a classe ViewController e herda a classe UIViewController e adota o protocolo AddItemViewControllerDelegate

No método addItem(_:), inicializamos uma instância da classe AddItemViewController, definimos a propriedade delegate e a apresentamos ao usuário. Note que temos todos os métodos delegados do protocolo AddItemViewControllerDelegate implementados. Se não tivemos, o compilador irá informar a nós que a classe ViewController não está em conformidade com o protocolo AddItemViewControllerDelegate. Tente isto comentando um dos métodos delegado.

Swift Protocol Implementation Warning

Conclusão

Delegação é um padrão que você vai se deparar com frequência quando estiver desenvolvendo aplicativos iOS e OS X. O Cocoa depende muito desse padrão de projeto e é importante familiarizar-se com ele.

Desde a introdução dos blocos, há alguns anos, a Apple lentamente oferece uma API alternativa baseada em blocos para algumas implementações de delegação. Alguns desenvolvedores seguiram a liderança da Apple, oferecendo suas próprias alternativas baseadas em blocos. A popular biblioteca AFNetworking, por exemplo, baseia-se fortemento em blocos ao invés de delegação, resultando em uma elegante e intuitiva API.

Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.