Design Pattern para iOS – Parte 1

Este tutorial foi traduzido do site: http://www.raywenderlich.com/46988/ios-design-patterns.
Embora o post original seja de 2013 a importancia dos conceitos de Design do código são importantes, para manter a legibilidade e a reutilização do código. E principalmente, utilizado para equipes que precisem trabalhar com códigos grandes, e que um precise entender exatamente como o outro programador implementou um determinado ponto do projeto.

Esta tradução pode conter erros, e se puderem, podem apontar esses erros comentando o post!

Design Patterns são soluções reutilizáveis ​​para problemas comuns em design de software. Eles são modelos concebidos para ajudá-lo a escrever um código que é fácil de entender e reutilizar. Eles também ajudam a criar um código de baixo acoplamento para que você possa alterar ou substituir componentes em seu código sem muito aborrecimento.

Se você é novo em design patterns, então eu tenho boas notícias para você! Em primeiro lugar, você já está usando toneladas de iOS design patterns graças à forma como Cocoa é construído e as melhores práticas que você é encorajado a usar. Em segundo lugar, este tutorial vai lhe trazer até a velocidade em todas as grandes (e não tão grandes) padrões iOS de projeto que são comumente usados ​​em Cocoa.

O Tutorial é dividido em Secções, uma seção por designe pattern. Em cada seção, você irá ler uma explicação dos seguintes itens:

– O que é o design patterns

– Por que você deve usá-lo

– Como usá-lo e, sempre que adequado, armadilhas comuns para observar ao usar o padrão.

Neste tutorial você irá criar uma biblioteca de música, que irá mostrar seus albuns e outras informações relevantes.

Neste processo de desenvolver este app, você vai se familiarizar com os padrões mais comuns de design Cocoa:

Criacional: Singleton e Abstract Factory.

Estrutural: MVC, Decorator, Adapter, Facade e Composite.

Comportamental: Observer, Memento, Chain of Responsibility and Command.

Não se deixe enganar em pensar que este é um artigo sobre a teoria; você vai ter que usar a maioria desses padrões de projeto em seu aplicativo de música. Seu aplicativo será parecido com este no final do tutorial:

1

Baixe o projeto inicial, extraia o conteudo do arquivo zip, e abra o arquivo BlueLibrary.xcode-proj no Xcode.

Não há muita coisa, é apenas um viewController padrão e um simples HTTP Client com a implementação vazia.

Antes de mergulhar em designe Pattern, voê deve criar duas classes para guardar e mostrar os dados dos albuns.

Navega até File\New\File. Selecione iOS > Cocoa Touch e então Objective-C Class e click em Next. Coloque o nome da classe para Album e a subclasse NSObject.

Abra o arquivo Algum.h e adicione a seguinte propriedade e protótipo de método:

@property (nonatomic, copy, readonly) NSString *title, *artist, *genre, *coverurl, *year;
-(id) initWithTitle:(NSString*)title artist:(NSString*)artist coverurl:(NSString*) coverUrl year:(NSString*)year;

Note que todas as propriedades são somente-leitura (readonly), a partir que elas não precisam ser mudadas depois que o objeto Album for criado.

O método é o inicializador do objeto. Quando você criar um novo álbum, você irá passar o nome do álbum, o artista, a url para capa do álbum, e o ano.

Agora abra o Álbum.m e adicione o seguinte implementação do método:

 -(id)initWithTitle:(NSString*) title artist:(NSString*) artist coverUrl:(NSString*)coverUrl year:(NSString*)year</code>

{
self = [super self];
if(self) {
_title = title;
_artist = artist;
_vocerUrl = coverUrl;
_year = year;
_genre = @”Pop”;
}
return self;
}

Não há nada de sofisticado aqui, apenas um simples método para criar uma nova instância do método.
Novamente, crie um novo arquivo, e então coloque o nome de AlbumView e desta vez a subclasse será UIView.
Abra AlbumView.h e adicione o seguinte método:

 -(id) initWithFrame:(CGRect)frame albumCover:(NSString*)albumCover;

Agora abra o AlbumView.m e subistitua toda a implementação com o seguinte código:

@implementation AlbumView</code>{

UIImageView *coverImage;
UIActivityIndicatorView* indicator;
}

-(id)initWithFrame:(CGRect)frame albumCover:(NSString*)albumCover{

self = [super initiWithFrame:frame];
if(self){

self.backgroundColor = [UIColor blackColor];
// a coverImage possui 5 pixels de margem a partir de cada frame
coverImage = [[UIIMageView alloc] initWithFrame: CGRectMake(5, 5, frame.size.width-10, frame.size.height-10)];
[self addSubview: coverImage];
indicator = [[UIActivityIndicatorView alloc] init];
indicator.center = self.center;
indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[indicator startAnimating];
[self addSubview:indicator];
}

return self/

}
@end

A primeira coisa que você deve notar aqui é a instancia de variável chamada coverImage. Esta variável representa a imagem da capa do album. A segunda variável é um indicador que gira para indicar atividade enquanto a capa está sendo baixada.

Na implementaçaõ do inicializador, você configurou o fundo para preto, criou o image view com uma pequena margem e 5 pixels e criou e adicionou um indicador de atividade.

MVC – O Rei do Designe Patterns

2

 

Model View Controller(MVC) é uma construção em blocos de Cocoa e é inquestinávelmente é o designe patterns mais usado. Ele classifica os objetos de acordo com seu papel geral em seu aplicativo e encoraja a clara separação do código baseado nesse papel.

As regras:

Model: O objeto que guarda os dados de sua aplicação, e define como manipulalas. Por exemplo, em seu app o Model é sua classe Album.

View: Os objetos que estão responsável pela representação visual do Model e controles do usuário, basicamente, todos são subclasses de UIView. Em sua aplicação o View é representado pela classe AlbumView.

Controller: O controller é o intermediador que coordena todo o trabalho. Ele acessa os dados do model e mostra-os usando as views, escuta os eventos e manipula os dados necessários. No nosso caso ela é a classe ViewController.

Uma boa implementação deste DP em sua aplicação siginifica que cada objeto está dentro de um desses grupos.

A comunicação entre a View e o Model através do Controller pode ser melhor descrita com o seguinte diagrama:

3

O modelo notifica o Controller de qualquer alteração nos dados, e por sua vez, o Controller atualiza os dados na View. A View pode então notificar o Controller de ações que o usuário realizou e o Controller irá então atualizar o Model, se necessário ou recuperar qualquer dado requisistado.

Você pode estar se perguntando por que você não pode simplesmente abandonar o Controller, e implementar a Visão e Modelo na mesma classe, como que parece muito mais fácil.

Tudo se resume a separação e reutilização de código. O ideal é que a View deve ser completamente separada do Model.. Se o View não depende de uma implementação específica do modelo, em seguida, ela pode ser reutilizada com um Model diferente para apresentar alguns outros dados.

Como usar o padrão MVC

Primeiro, você precisa ter certeza que cada classe em seu projeto está seja um Controller, um Model ou a View; não combine a funcionalidade de 2 classes em uma. Você já fez um bom trabalho até agora, criando uma classe Album e uma classe AlbumView.

Em segundo lugar, a fim de assegurar que você se conforma com este método de trabalho você deve criar três grupos de projetos para manter o seu código, um para cada categoria.

Vá em File\New\Group e crie um grupo chamado Model, outro chamado View, e depois outro Controller.

Agora coloque Album.h e Album.m para o grupo Model. Coloque AlbumView.h e AlbumView.m para grupo View e ViewController.h e ViewController.m para o grupo Controller.

Ficando mais ou menos assim:

4

Agora que seus componentes estão organizados, você precisa obter os dados do álbum de algum lugar. Você vai criar uma classe API para usar todo o seu código para gerenciar os dados – que apresenta uma oportunidade para discutir o seu próximo padrão de design – o Singleton.

Singleton Pattern

O padrão de projeto Singleton garante que existe apenas uma instância de uma determinada classe e que há um ponto de acesso global a essa instância. Ele geralmente usa o carregamento lento para criar a única instância quando é necessário na primeira vez.

Você provavelmente está se perguntando por que você se importa se há mais de uma instância de uma classe flutuando. Código e memória é barato, certo?

Há alguns casos em que se faz sentido ter exatamente uma instância de uma classe. Por exemplo, não há necessidade de ter várias instâncias Logger por ai, a menos que você quiser escrever para vários arquivos de log ao mesmo tempo. Ou então, tomar uma classe mundial manipulador de configuração: é mais fácil de implementar um acesso thread-safe a um único recurso compartilhado, como um arquivo de configuração, do que ter muitas classes modificando o arquivo de configuração, possivelmente, ao mesmo tempo.

Como usar Singleton Pattern

Dê uma olhada no diagrama abaixo:

5

A imagem acima mostra uma classe Logger com uma única propriedade, e dois métodos: sharedInstace e init.

A primeira vez que um cliente chamar sharedInstance, a propriedade instance ainda não foi inicializada, então você cria uma nova instancia da classe e retorna uma referencia para ela.

Na próxima vez que você chamar sharedInstance, instance é imediatamente retornada sem qualquer tipo de inicialização. Essa lógica garante que somente uma instância exista a todo momento.

Você irá implementar esse padrão ao criar uma classe singleton para gerenciar todos os dados dos albuns.

Você vai notar que há um grupo chamado API no projeto; este é o lugar onde você vai colocar todas as classes que prestará serviços ao seu aplicativo. Dentro deste grupo crie uma nova classe iOS\Cocoa Touch\Objective-C. Nomeie de LibraryAPI, e será uma subclasse de NSObject.

Abra LibraryAPI.h e substitua o conteudo com o seguinte código:

@interface LibraryAPI : NSObject</code>

+(LibraryAPI*) sharedInstance;

@end

Agora vá em LibraryAPI.m e insira este codigo:
<code>
+(LibraryAPI*)sharedInstance</code>

{

// 1

static LibraryAPI* _sharedInstance = nil;

// 2

dispatch_one_t oncePredicate;

// 3

dispatch_once(&amp;oncePredicate, ^{

_sharedInstance = [[LibrayAPI alloc]init];

});

return _sharedInstance;

}

Há muita coisa acontecendo neste método curto:

1 – Declara uma variável estática para guardar uma instancia da sua classe, garantindo que está disponível globalmente dentro de sua classe.

2 – Declara a variável estática dispatch_once_t que assegura que o código de inicialização executa somente uma vez.

3 – Usa Grand Central Dispatch para executar um bloco que inicializa uma instância de LibraryAPI. Isso é a essência do design pattern Singleton: a inicialização não será chamada novamente desde que a classe já tenha sido instanciada.

A próxima vez que você chamar sharedInstance, o código dentro do bloco dispatch_once não será executada (uma vez que já foi executado uma vez) e você recebe uma referência para a instância criada anteriormente de LibraryAPI.

Note: To learn more about GCD and its uses, check out the tutorials Multithreading and Grand Central Dispatch and How to Use Blocks on this site.

Nota: Para aprender mais sobre GCD (Grand Central Dispatch) e usá-lo, olhe os tutoriais nos seguintes links Multithreading and Grand Central Dispatch e How to Use Blocks.

Agora você tem um objeto Singleton como ponto de entrada para gerenciar os álbuns. Dê um passo adiante e crie uma classe para lidar com a persistência dos dados da biblioteca.

Crie uma nova classe no grupo API e coloque o nome de PersistencyManager, e subclasse de NSObject.

Abra PersistencyManager.h e adicione o seguinte import no topo:

#import “Album.h”

E agora adicione o seguinte codigo no arquivo PersistencyManager.h, apos a linha @interface:

-(NSArray*)getAlbums;

-(void)addAlbum:(Album*)album atIndex:(int)index;

-(void)deleteAlbumAtIndex:(int)index;

Acima, são protótipos de três métodos que você irá precisar para manipular os dados do album.

Agora abra o arquivo PersistenceManager.m e coloque esse código após a linha @implementation:

@interface PersistenceManager(){

// um array de todos os albuns

NSMutableArray* albums;

}

Acima adicionamos uma extenção de uma classe, que é outro modo de adicionar métodos e variáveis privadas para uma classe sem que classes externas saibam que elas existam. Aqui você delcarou um NSMutableArray para guardar os dados dos albums. O array é mutável, sendo fácil adicionar e deletar albums.

Agora adicione o seguinte codigo de implementação no arquivo PersistencyManager.m depois da linha @implementation>

- (id)init
{
self = [super init];
if (self) {
<em>// a dummy list of albums</em>
albums=[<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/">NSMutableArray</a> arrayWithArray:
@[[[Album alloc] initWithTitle:<em>@</em>"Best of Bowie" artist:<em>@</em>"David Bowie" coverUrl:<em>@</em>"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:<em>@</em>"1992"],
[[Album alloc] initWithTitle:<em>@</em>"It's My Life" artist:<em>@</em>"No Doubt" coverUrl:<em>@</em>"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:<em>@</em>"2003"],
[[Album alloc] initWithTitle:<em>@</em>"Nothing Like The Sun" artist:<em>@</em>"Sting" coverUrl:<em>@</em>"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:<em>@</em>"1999"],
[[Album alloc] initWithTitle:<em>@</em>"Staring at the Sun" artist:<em>@</em>"U2" coverUrl:<em>@</em>"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:<em>@</em>"2000"],
[[Album alloc] initWithTitle:<em>@</em>"American Pie" artist:<em>@</em>"Madonna" coverUrl:<em>@</em>"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:<em>@</em>"2000"]]];
}
return self;
}

Em init você irá popular o array com cinco exemplos de álbuns. Se os álbuns acima não são do seu gosto, fique a vontade para alterá-los.

Agora adicione os seguintes três métodos a PersistencyManager.m

-(NSArray*)getAlbums

{

return albums;

}

-(void)addAlbum:(ALbum*)album atIndex:(int)index

{

if (albums.count &gt;= index)
[albums insertObject:album atIndex:index];
else
[albums addObject:album];
}

- (void)deleteAlbumAtIndex:(int)index
{
[albums removeObjectAtIndex:index];
}

Estes métodos permitem obter, adicionar e excluir álbuns.

Crie seu projeto apenas para se certificar que tudo ainda compila corretamente.

Neste ponto, você pode se perguntar onde a classe PersistencyManager vem, uma vez que não é um Singleton. A relação entre LibraryAPI e PersistencyManager será mais explorada na próxima seção onde você vai olhar para o padrão de projeto Facade.

Facade Design Pattern

6

O padrão de projeto Facade (Fachada) fornece uma única interface simples para um subsistema complexo. Em vez de expor o usuário a um conjunto de classes e suas APIs, você somente irá expor uma API unificada simples.

A imagem a seguir explica esse conceito:

7

O usuário da API é completamente inconsciente da complexidade que se encontra escondido. Este padrão é ideal quando se trabalha com um grande número de classes, especialmente quando elas são complicados de usar ou difícil de entender.

O padrão Facade desacopla o código que usa o sistema da interface e da implementação da classe que você está escondendo; isso também reduz a dependência de código externo do trabalho interno do seu subsistema. Isso também é útil se a classe sob a fachada são suscetíveis a mudanças, como a classe de fachada pode manter a mesma API enquanto as coisas mudam por trás.

Por exemplo, se um dia você quiser trocar seu backend service, você não precisa mudar o código que usa a sua API, já que você não precisa alterá-la.

Como usar Facade Pattern

Atualmente você tem PersistencyManager para salvar os dados dos álbuns localmente e HTTPClient para manusear a comunicação remota. As outras classes em seu projeto não devem estar cientes dessa lógica.

Para implementar esse padrão, apenas LibraryAPI deve manter as instâncias da PersistencyManager e HTTPClient. Então, LibraryAPI irá expor uma API simples para acessar esses serviços.

Nota: Normalmente, existe um singleton para o tempo de vida do aplicativo. Você não deve ter muitas ponteiros fortes no singleton para outros objetos, uma vez que não será liberado até que o aplicativo seja fechado.

O design ficará como isso:

8

LibraryAPI vai ser exposta a outro código, mas vai esconder o HTTPClient e a complexidade de PersistencyManager do resto do aplicativo.

Abra LibraryAPI.h e adicione o import:

#import “Album.h”

Agora, adicione as seguintes definições de métodos em LibraryAPI.h:

- (<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/">NSArray</a>*)getAlbums;
- (void)addAlbum:(Album*)album atIndex:(int)index;
- (void)deleteAlbumAtIndex:(int)index;

Agora, estes são os métodos que você vai expor para outras classes.

Vá em LibraryAPI.m e adicione estes dois imports:

#import "PersistencyManager.h"
#import "HTTPClient.h"

Isso vai ser somente o lugar onde você importa essas classes. Lembre-se: sua API será somente um ponto de acesso para seu sistema “complexo”.

Agora, adicione algumas variáveis privadas através de uma extensão de classe (abaixo da linha @implementation):

@interface LibraryAPI () {
PersistencyManager *persistencyManager;
HTTPClient *httpClient;
BOOL isOnline;
}
@end

isOnLine determina se o servidor está atualizado com qualquer mudança feita na lista de album, tal como albuns adicionados ou apagados.

Você agora precisa inicializar estas variáveis via init. Adicione o código em LibraryAPI.m

- (id)init
{
self = [super init];
if (self) {
persistencyManager = [[PersistencyManager alloc] init];
httpClient = [[HTTPClient alloc] init];
isOnline = NO;
}
return self;
}

O cliente HTTP na verdade não funciona com um servidor real e só está aqui para demonstrar o uso do padrão fachada, assim isOnline será sempre não.

Agora, adicione os três seguintes métodos em LibraryAPI.m:

- (<a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/">NSArray</a>*)getAlbums
{
return [persistencyManager getAlbums];
}

- (void)addAlbum:(Album*)album atIndex:(int)index
{
[persistencyManager addAlbum:album atIndex:index];
if (isOnline)
{
[httpClient postRequest:<em>@</em>"/api/addAlbum" body:[album description]];
}
}

- (void)deleteAlbumAtIndex:(int)index
{
[persistencyManager deleteAlbumAtIndex:index];
if (isOnline)
{
[httpClient postRequest:<em>@</em>"/api/deleteAlbum" body:[@(index) description]];
}
}

Dê uma olhada em addAlbum: atIndex :. A classe atualiza primeiro os dados localmente, e em seguida, se houver uma conexão com a internet, ele atualiza o servidor remoto. Esta é a verdadeira força da Facade; quando alguma classe fora do seu sistema adiciona um novo álbum, ele não sabe – e não precisa saber – da complexidade que se encontra por baixo.

Nota: Ao projetar uma Facade para as classes em seu subsistema, lembre-se que nada impede que o cliente de acessar essas classes “escondidos” diretamente. Não seja mesquinho com o código de defesa e não presuma que todos os clientes vão necessariamente utilizar suas classes da mesma forma que a Facade utiliza-los.

Crie e execute o aplicativo. Você verá uma tela preta vazia incrivelmente emocionante como este:

9

Você vai precisar de algo para exibir os dados do álbum na tela – o que é um uso perfeito para o seu próximo padrão de design: o Decorator.

Acompanhe a segunda parte do Tutorial no link: Design Pattern para iOS – Parte 2

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s