Advertisement
  1. Code
  2. Swift

Programação orientada a protocolo em Swift 2

Scroll to top
Read Time: 9 min

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

Introdução

Com o lançamento do Swift 2, a Apple adicionou uma lista de novas caracteristicas e capacidades para a linguagem de programação Swift. Uma das mais importantes, entretanto, foi uma revisão dos protocolos. A melhoria da funcionalidade disponível com os protocolos Swift permitem um novo tipo de programação, programação orientada a protocolo. Isto está em contraste com o estilo mais comum, programação orientada a objeto que muitos de nós estamos acostumados.

Neste tutorial, eu vou te mostrar o básico da programação orientada a protocolo em Swift e como ela se difere da programação orientada a objetos.

Pré-requisitos

Este tutorial requer que você esteja rodando o Xcode 7 ou superior, que inclui suporte à versão 2 da linguagem de programação Swift.

1. Básico de protocolo

Se você não estiver familiarizado com os protocolos, eles são uma maneira de estender a funcionalidade de uma classe ou estrutura existente. Um protocolo pode ser pensado como um escopo ou interface que define um conjunto de propriedades e métodos. Para uma classe ou estrutura estar em conformidade com um protocolo é necessário preencher essas propriedades e métodos com valores e implementações, respectivamente.

Também deve ser notado que qualquer uma dessas propriedades e métodos podem ser designados como opcional, o que significa que tipos em conformidade não são obrigados implementá-las. A definição de protocolo e uma classe em conformidade em Swift, ficaria desta forma:

1
protocol Welcome {
2
    var welcomeMessage: String { get set }
3
    optional func welcome()
4
}
5
6
class Welcomer: Welcome {
7
    var welcomeMessage = "Hello World!"
8
    func welcome() {
9
        print(welcomeMessage)
10
    }
11
}

3. Um exemplo

Para começar, abra o Xcode e crie um novo playground para iOS ou OS X. Uma vez que o Xcode tenha criado o playground, substitua seu conteúdo conforme abaixo:

1
protocol Drivable {
2
    var topSpeed: Int { get }
3
}
4
5
protocol Reversible {
6
    var reverseSpeed: Int { get }
7
}
8
9
protocol Transport {
10
    var seatCount: Int { get }
11
}

Nós definimos três protocolos, cada um contendo uma propriedade. Em seguida, nós criamos uma estrutura que estará em conformidade com estes três protocolos. Adicione o código abaixo no playground.

1
struct Car: Drivable, Reversible, Transport {
2
    var topSpeed = 150
3
    var reverseSpeed = 20
4
    var seatCount = 5
5
}

Você deve ter notado que, ao invés de criar uma classe em conformidade com estes protocolos, criamos uma estrutura. Fazemos isto para evitar um dos problemas típicos inerentes da programação orientada a objeto, as referências de objeto.

Imagine, por exemplo, que você tem dois objetos, "A" e "B". O "A" cria alguns dados e mantém uma referência a esses dados. "A" então compartilha estes dados com "B" por referência, o que significa que ambos tem uma referência para o mesmo objeto. Sem o "A" saber, o "B" altera o dado de alguma maneira.

Apesar disto não parecer um grande problema, isto pode ser quando "A" não esperar que os dados possam ser alterados. O objeto "A" pode encontrar esses dados sem saber como trata-los ou lidar com eles. Isto é um risco comum na referência de objetos.

Em Swift, estruturas são passadas como valor ao invés de referência. Isso significa que, em nosso exemplo acima, se os dados criados em "A" forem empacotados como uma estrutura ao invés de um objeto e compartilhado com "B", os dados seriam copiadas ao invés de compartilhados por referência. E como resultado, ambos "A" e "B" teriam sua própria copia unica dos mesmos dados. Uma alteração feita por "B" não afetaria a copia controlada por "A".

Quebrando os componentes Drivable, Reversible e Transport em protocolos individuais, também permite um maior nível de personalização que a herança de classe tradicional. Se você leu meu primeiro tutorial sobre o novo framework Gameplaykit em iOS9, então este modelo orientado a protocolo é muito similar à estrutura Entidades e Componentes usado no framework GameplayKit.

Adotando essa abordagem, tipos de dados personalizados podem herdar a funcionalidade de várias fontes, ao invés de uma única superclasse. Tendo em mente o que temos até agora, poderíamos criar as seguintes classes:

  • uma classe com os componentes dos protocolos Drivable e Reversible.
  • uma classe com os componentes dos protocolos Drivable e Transportable.
  • uma classe com os componentes dos protocolos Reversible e Transportable.

Com programação orientada a objeto, a maneira mais lógica para criar essas três classes seria herdar de uma superclasse que contém os componentes de todos os três protocolos. Esta abordagem, entretanto, resulta em uma superclasse mais complicada do que ela precisa ser e cada herança da superclasse com mais funcionalidades do que elas precisam.

3. Extensões de protocolo

Tudo o que eu te mostrei até agora tem sido possível no Swift desde seu lançamento em 2014. Estes mesmos conceitos de orientação a protocolos podem ainda ser aplicados a protocolos de Objective-C. Devido às limitações que existiam em protocolos, no entanto, a verdadeira programação orientada a protocolo não foi possível até um certo número de características-chave serem adicionados à linguagem Swift na versão 2. Um dos mais importantes desses recursos é a extensões de protocolo, incluindo as extensões condicionais.

Primeiramente, vamos estender o protocolo Drivable e adicionar uma função para determinar se um determinado Drivable (dirigível) está mais rápido que outro. Adiciona o seguinte em seu playground:

1
extension Drivable {
2
    func isFasterThan(item: Drivable) -> Bool {
3
        return self.topSpeed > item.topSpeed
4
    }
5
}
6
7
let sedan = Car()
8
let sportsCar = Car(topSpeed: 250, reverseSpeed: 25, seatCount: 2)
9
10
sedan.isFasterThan(sportsCar)

Você pode ver que, quando o código for executado no playground, ele exibe um valor false, já que o seu carro sedan tem um valor padrão para topSpeed de 150, que é menor que o do sportsCar.

Extension outputExtension outputExtension output

Você deve ter notado que nós fornecemos uma definição de função, ao invés de uma declaração de função. Isto pode parece estranho, por que os protocolos deveriam apenas conter declarações. Certo? Está é outra característica importante das extensões de protocolo em Swift 2, comportamentos padrão. Estendendo um protocolo, você pode fornecer uma implementação padrão para funções e propriedades computados de modo que as classes em conformidade com o protocolo não precisem implementa-las.

Em seguida, nós iremos definir outra extensão do protocolo Drivable, mas desta vez nós definiremos apenas para tipos de valor que também estejam em conformidade com o protocolo Reversible. Esta extensão irá conter uma função que determina qual objeto está com a maior faixa de velocidade. Nós podemos conseguir isto com o código abaixo:

1
extension Drivable where Self: Reversible {
2
    func hasLargerRangeThan(item: Self) -> Bool {
3
        return (self.topSpeed + self.reverseSpeed) > (item.topSpeed + item.reverseSpeed)
4
    }
5
}
6
7
sportsCar.hasLargerRangeThan(sedan)

A palavra-chave Self, iniciada com um "S" maiúsculo, é usado para representar que a classe ou estrutura precisa estar em conformidade com o seguinte protocolo. No exemplo acima, a palavra-chave Self representa a estrutura Car.

Após rodar o código no playground, o Xcode irá exibir o resultado em sua barra lateral direito com o resultado. Note que o sportsCar tem um range maior que o sedan.

Conditional extension outputConditional extension outputConditional extension output

4. Trabalhando com a biblioteca padrão do Swift

Apesar de definir e extender seus próprios protocolos possa ser muito usual, o verdadeiro poder da extensão de protocolo aparece quando trabalhamos com a biblioteca padrão do Swift. Isto permite a você adicionar propriedades ou funções à protocolos existentes, tal como CollectionType (usado por arrays e dicionarios) e Equatable (capaz de determinar quando dois objetos são iguais). Com extensões condicional de protocolo, você também pode fornecer funcionalidade especifica para tipos especificos de objetos em conformidade com um protocolo.

Em nosso playground, nós iremos estender o protocolo CollectionType e criar dois métodos, um para pegar a média de velocidade dos carros em um array de Car e outro para pegar a média da velocidade regressiva. Adicione o código a seguir em seu playground:

1
extension CollectionType where Self.Generator.Element: Drivable {
2
    func averageTopSpeed() -> Int {
3
        var total = 0, count = 0
4
        for item in self {
5
            total += item.topSpeed
6
            count++
7
        }
8
        return (total/count)
9
    }
10
}
11
12
func averageReverseSpeed<T: CollectionType where T.Generator.Element: Reversible>(items: T) -> Int {
13
    var total = 0, count = 0
14
    for item in items {
15
        total += item.reverseSpeed
16
        count++
17
    }
18
    return (total/count)
19
}
20
21
let cars = [Car(), sedan, sportsCar]
22
cars.averageTopSpeed()
23
averageReverseSpeed(cars)

A extensão de protocolo que define o método averageTopSpeed toma vantagem da extensão condicional do Swift 2. Em contraste, a função averageReverseSpeed que definimos diretamente abaixo é outra maneira de conseguir um resultado semelhante utilizando genéricos do Swift. Eu pessoalmente prefiro a visão limpa da extensão do protocolo CollectionType, mas é uma preferência pessoal.

Em ambas as funções, nós percorremos o array, somamos o valor no total e depois retornamos o valor médio. Note que nós manualmente guardamos o total de itens do array, por que quando trabalhamos com CollectionType ao invés do regular tipo Array, a propriedade count é um tipo de valor Self.Index.Distance ao invés de um Int.

Uma vez que seu playground tenha executado todo este código, você poderá ver uma saída da velocidade média superior de 183 e uma velocidade média reversa de 21.

Standard Library extensionsStandard Library extensionsStandard Library extensions

5. Importância das Classes

Apesar da programação orientada a protocolo ser uma maneira muito eficiente e escalável para gerenciar o seu código em Swift, existem ainda razões perfeitamente válidas para usar classes ao desenvolver em Swift:

Compatibilidade

A maioria dos SDKs do iOS, watchOS e tvOS são escritos em Objetive-C, usando uma abordagem orienta a objeto. Se você precisa interagir com qualquer API incluída nestes SKDs, você será forçado a usar as classes definidas nesses SDKs.

Referenciando um arquivo ou item externo.

O compilador Swift otimiza o tempo de vida dos objetos baseado em quando e onde eles serão usados. A estabilidade de objetos de classe significa que suas referências a outros arquivos e itens permanecerão consistentes.

Referências de Objetos

Referências de objetos são exatamente o que você precisa, às vezes, por exemplo, se você está alimentando informações em um determinado objeto, como um processador de gráficos. Usar classes com compartilhamento implícito é importante em situações como essa, porque você precisa ter certeza de que você ainda está enviando os dados para o mesmo processador de antes.

Conclusão

Espero que no final deste tutorial você posse ver o potencial da programação orientada a protocolo em Swift e como ele pode ser usado para agilizar e estender seu código. Apesar desta nova metodologia de codificação não substituir totalmente a programação orientada a objeto, traz uma série de possibilidades novas, muito úteis.

Com o comportamento padrão para extensões do protocolo, programação orientada a protocolos em Swift será adotado por várias APIs futuras e irá mudar completamente a maneira em que nós pensamos sobre desenvolvimento de software.

Como sempre, deixar seus comentários e feedback nos comentários abaixo.

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.