Advertisement
Scroll to top
Read Time: 11 min

() translation by (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.

1
#import <UIKit/UIKit.h>

2
3
@protocol AddItemViewControllerDelegate;
4
5
@interface AddItemViewController : UIViewController
6
7
@property (weak, nonatomic) id<AddItemViewControllerDelegate> delegate;
8
9
@end
10
11
@protocol AddItemViewControllerDelegate <NSObject>
12
- (void)viewControllerDidCancel:(AddItemViewController *)viewController;
13
- (void)viewController:(AddItemViewController *)viewController didAddItem:(NSString *)item;
14
15
@optional
16
- (BOOL)viewController:(AddItemViewController *)viewController validateItem:(NSString *)item;
17
@end

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.

1
- (IBAction)cancel:(id)sender {
2
    if (self.delegate && [self.delegate respondsToSelector:@selector(viewControllerDidCancel:)]) {
3
        [self.delegate viewControllerDidCancel:self];
4
    }
5
}

É 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.

1
- (IBAction)addItem:(id)sender {
2
    // Initialize View Controller

3
    AddItemViewController *viewController = [[AddItemViewController alloc] init];
4
    
5
    // Configure View Controller

6
    [viewController setDelegate:self];
7
    
8
    // Present View Controller

9
    [self presentViewController:viewController animated:YES completion:nil];
10
}
1
- (void)viewControllerDidCancel:(AddItemViewController *)viewController {
2
    // Dismiss Add Item View Controller

3
    ...
4
}

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.

1
#import "AddItemViewController.h"

2
3
@interface ViewController () <AddItemViewControllerDelegate>
4
5
@end

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.

1
import UIKit
2
3
protocol AddItemViewControllerDelegate: NSObjectProtocol {
4
    func viewControllerDidCancel(viewController: AddItemViewController)
5
    func viewController(viewController: AddItemViewController, didAddItem: String)
6
    func viewController(viewController: AddItemViewController, validateItem: String) -> Bool
7
}
8
9
class AddItemViewController: UIViewController {
10
    var delegate: AddItemViewControllerDelegate?
11
    
12
    func cancel(sender: AnyObject) {
13
        delegate?.viewControllerDidCancel(self)
14
    }
15
}

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

1
import UIKit
2
3
class ViewController: UIViewController, AddItemViewControllerDelegate {
4
    func addItem(send: AnyObject) {
5
        // Initialize View Controller

6
        let viewController = AddItemViewController()
7
        
8
        // Configure View Controller

9
        viewController.delegate = self
10
        
11
        // Present View Controller

12
        presentViewController(viewController, animated: true, completion: nil)
13
    }
14
    
15
    func viewControllerDidCancel(viewController: AddItemViewController) {
16
        // Dismiss Add Item View Controller

17
        ...
18
    }
19
    
20
    func viewController(viewController: AddItemViewController, didAddItem: String) {
21
        
22
    }
23
    
24
    func viewController(viewController: AddItemViewController, validateItem: String) -> Bool {
25
        
26
    }
27
}

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 WarningSwift Protocol Implementation WarningSwift 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
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
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.