Advertisement
  1. Code
  2. Mobile Development

Padrões de Projeto: Singletons

Scroll to top
Read Time: 9 min

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

Padrões de Projeto é muitas vezes considerado um tópico muito avançado e, portanto, ignorado ou esquecido por pessoas novas em desenvolvimento de iOS e OS X. No entanto, há uma série de Padrões de Projeto que todo aspirante a desenvolvedor iOS ou OS X enfrentará desde o primeiro dia. O padrão singleton é um destes padrões.

Antes de começarmos a olhar as técnicas de implementação do padrão singleton em Swift e Objective-C, eu gostaria de tirar um momento para entender o padrão singleton, incluindo suas vantagens e desvantagens.

1. O que é um Singleton?

Singletons Verdadeiros & Funcionais 

O padrão singleton é inextricavelmente ligado à programação orientada a objetos. A definição do padrão singleton é simples, apenas uma instância da classe singleton pode ser criada ou estar viva por vez.

Bremt Simmons, entretanto, diferência está definição criando uma distinção entre singletons verdadeiros e singletons funcionais. Brent define singletons verdadeiros como classes que sempre retornam a mesma instância de si mesmo. Isto é, não é possível criar mais que uma instância da mesma classe. Singletons funcionais são criados uma vez e usada em vários lugares.

Eu tenho certeza que muitos desenvolvedores não categorizariam singletons funcionais como singletons, mas eu gosto da distinção que Brent faz. Singletons funcionais muitas vezes não tem as desvantagens típicas dos singletons verdadeiros. Nós veremos estas desvantagens mais tarde neste artigo.

Se você está familiarizado com o desenvolvimento em iOS ou OS X, você irá notar que os frameworks da Apple fazem uso do padrão singleton. Um aplicativo iOS, por exemplo, pode ter apenas uma instância da classe UIApplication, que você pode acessar através do método de classe sharedApplication.

1
UIApplication *sharedApplication = [UIApplication sharedApplication];
1
let sharedApplication = UIApplication.sharedApplication()

Ainda que a classe UIApplication te de acesso ao singleton UIApplication, nada impede você de instanciar explicitamente uma instância UIApplication.

1
UIApplication *newApplication = [[UIApplication alloc] init];
1
let newApplication = UIApplication()

O resultado, entretanto, é uma exceção em tempo de execução. A exceção diz claramente que apenas uma instância da classe UIApplication pode estar viva por vez. Em outras palavras, a classe UIApplication foi projetada com o padrão singleton em mente.

1
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'There can only be one UIApplication instance.'

Vantagens

Acessibilidade

A vantagem mais óbvia do padrão singleton é acessibilidade. Considere a classe UIApplication como exemplo. Ao importar o framework UIKit, o singleton UIApplication é acessível a partir de qualquer lugar do seu projeto. Dê uma olhada no exemplo a seguir com a subclasse UIViewController.

1
#import <UIKit/UIKit.h>

2
3
@interface ViewController : UIViewController
4
5
@end
1
#import "ViewController.h"

2
3
@implementation ViewController
4
5
#pragma mark -

6
#pragma mark View Life Cycle

7
- (void)viewDidLoad {
8
    [super viewDidLoad];
9
    
10
    // Access Application Singleton

11
    UIApplication *application = [UIApplication sharedApplication];
12
}
13
14
@end

Controle e Comportamento

Às vezes é útil ou essencial que apenas uma instância de uma classe esteja viva por vez. A classe UIApplication é um exemplo desta necessidade. Outro exemplo pode incluir classes para log, caching ou operações I/O.

Dito isto, é importante entender que singletons são um meio de realizar o controle e o comportamento. Técnicas de codificação defensivas podem te dar o mesmo resultado sem a sobrecarga mental do Padrão Singleton.

Desvantagens

Estado Global

Criando um singleton, você está criando essencialmente um estado global. É importante que você esteja ciente disso. A alguns anos atrás, Miško Hevery escreveu um ótimo artigo sobre o padrão singleton em que ele explica porque o padrão deve ser evitado. Miško enfatiza que o estado global é a principal razão do porque os singletons são prejudiciais.

No entanto, existem exceções. Por exemplo, singletons que são imutáveis são pouco prejudiciais. Você ainda criará o estado global, mas esse estado é imutável. Miško menciona também loggers como uma — um pouco — aplicação aceitável do padrão singleton ja que seu estado flui em uma direção, a partir de seu aplicativo para o agente de log.

Acoplamento Forte

A maioria das linguagens de programação orientadas a objeto consideram o forte acoplamento uma má prática. Em outras palavras, é recomendado desacoplar os módulos o máximo possivel. Isto coloca o padrão singleton em um lugar ruim e singletons são, portanto, considerados um má prática por muitos programadores respeitados. Alguns desenvolvedores vão tão longe a ponto de considerar ele um anti-padrão que deve ser evitado.

Para melhor entender o problema, considere o cenário a seguir. Você criou um aplicativo iOS com um componente de rede em que você acessa o singleton UIApplication. Seu aplicativo iOS fez tanto sucesso que você considera criar um aplicativo para o OS X. A classe UIApplication, entretanto, não é valida no OS X devido a falta do framework UIKit. Isso significa que você terá que refatorar ou modificar seu componente de rede do aplicativo iOS.

Há uma série de soluções para resolver este problema, mas o ponto que quero mostrar é que você está fortemente acoplado ao módulo de rede do framework UIKit, a classe UIApplication em particular.

Reusabilidade

Forte acoplamento muitas vezes está intimamente associado com a capacidade de reusabilidade. Um objeto gráfico fortemente acoplado é muitas vezes difícil de reutilizar sem refatoração. Reusabilidade fraca às vezes é inevitável, mas certamente é algo a ter em mente no desenvolvimento de software.

2. Criando um Singleton

Objective-C

Criar um singleton em Objective-C é muito fácil. No trecho de código a seguir, criamos e implementamos um classe de log, Logger. Acessamos o objeto singleton através do método de classe sharedLogger.

1
#import <Foundation/Foundation.h>

2
3
@interface Logger : NSObject
4
5
#pragma mark -

6
#pragma mark Class Methods

7
+ (Logger *)sharedLogger;
8
9
@end

A implementação é simples. Declaramos uma variável estática _sharedInstance que vai manter uma referência do objeto singleton. Chamamos dispatch_once, uma função Grand Central Dispatch, e passamos um bloco em que vamos inicializar uma instância da classe Logger. Armazenamos a referência da instância em _sharedInstance.

1
#import "Logger.h"

2
3
@implementation Logger
4
5
#pragma mark -

6
#pragma mark Class Methods

7
+ (Logger *)sharedLogger {
8
    static Logger *_sharedInstance = nil;
9
    static dispatch_once_t oncePredicate;
10
    dispatch_once(&oncePredicate, ^{
11
        _sharedInstance = [[self alloc] init];
12
    });
13
    
14
    return _sharedInstance;
15
}
16
17
@end

A função dispatch_once garante que o bloco que passamos é executado apenas uma vez durante a vida útil do aplicativo. Isto é muito importante, já que só queremos inicializar uma instância da classe Logger.

A variável oncePredicate que é passada como o primeiro argumento da função dispatch_once é usado pela função dispatch_once para garantir que o bloco será executado apenas uma vez.

O método da classe sharedLogger retorna a instância Logger, retornando a referência armazenada em _sharedInstance. A implementação da sharedLogger pode parecer assustadora no começo, mas tenho certeza que você concorda que a idéia é muito simples.

Swift

Para implementar o padrão singleton em Swift, vamos fazer uso de classe e constantes, que foram introduzidas no Swift 1.2. Se você tiver problemas com o trecho de código a seguir, certifique-se que está usando o Xcode 6.3+.

1
class Logger {
2
    static let sharedInstance = Logger()
3
}

Deixe-me guiá-lo através da curta implementação da classe Logger. Nós iniciamos declarando uma classe chamada Logger. Para implementar o padrão singleton, nós declaramos uma propriedade de tipo, sharedInstance, do tipo Logger.

Fazendo uso da palavra-chave static, a constante sharedInstance é ligada a classe Logger ao invés das instâncias da classe. A constante sharedInstance é referenciada como uma propriedade de tipo já que ela é ligada ao tipo, isto é, a classe Logger. Fazemos o acesso ao objeto singleton através da classe Logger.

1
Logger.sharedInstance

Você pode estar se perguntando por que não usamos a função dispatch_once como fizemos na implementação de Objective-C. Debaixo dos panos, o Swift já usa dispatch_once ao inicializar constantes estáticas. Isso é algo que você tem de graça na Swift.

Outra abordagem comum para a implementação do padrão singleton é através do encadeamento de tipos. No exemplo a seguir, nós declaramos uma propriedade de tipo computada, sharedInstance, do tipo Logger. Na clausura da propriedade de tipo computada, nós declaramos uma estrutura chamada Singleton. A estrutura define uma propriedade de tipo constante, instance, do tipo Logger.

1
class Logger {
2
    class var sharedInstance: Logger {
3
        struct Singleton {
4
            static let instance: Logger = Logger()
5
        }
6
        
7
        return Singleton.instance
8
    }
9
}

O acesso ao objeto singleton é o mesmo para ambas as estratégias. Se você está usando Swift 1.2, então eu recomento usar a primeira abordagem por conta da sua brevidade e simplicidade. 

3. Quando usar Singletons?

Suponho que seja tentador, se a única ferramenta que você tem é um martelo, tratar tudo como se fosse um prego. — Abraham Maslow

Quando eu ouvi pela primeira vez sobre o padrão singleton, eu fiquei animado em como seria útil em meu próximo projeto. É muito tentador usar uma ferramenta que parece resolver muitos problemas. A primeira vista, o padrão singleton vai parecer um bala de prata ou um martelo de ouro, mas não é isso que o padrão singleton é.

Por exemplo, se você está usando um singleton apenas para torna-lo facilmente acessível, você pode estar usando o padrão singleton de formar errada. Não é porque a Apple usa o padrão singleton em seus frameworks que ele seja a solução certa para todos os problemas.

Singletons pode ser muito útil, mas é considerada um anti-padrão por muitos programadores experientes. A maioria deles tiveram más experiências com o padrão para chegar a essa conclusão. Aprenda com seus erros e use com moderação.

Eu não considero o padrão singleton um anti-padrão, mas ele é um padrão que deve ser usado com atenção. Se você se sentir tentado a transformar um objeto em um singleton, se pergunte se não há outra maneira de resolver o seu problema. Na maioria dos casos, o padrão singleton não é a unica solução válida. Uma solução alternativa pode exigir mais trabalho ou algum processo adicional, mas é provavelmente melhor a longo prazo.

Os singletons funcionais referenciados por Brent Simmons, muitas vezes são a alternativa perfeitamente válida para os singletons verdadeiros. Não há nada de errado com a criação de uma instância de uma classe e passá-la para os objetos que precisam dela. Considere esta abordagem na proxima vez que você estiver prestes a criar outro singleton.

Conclusão

O padrão singleton é simples e poderoso, mas ele deve ser usado com moderação. Alguns dos seus colegas podem aconselhá-lo contra isso, mas eu penso que é importante considerar o seu uso e julgar por si mesmo se você acha que é uma adição útil para sua caixa de ferramentas.

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.