Article image
Karol Attekita
Karol Attekita09/03/2023 16:23
Compartilhe

Entendendo a Programação Orientada a Protocolos em Swift

    Fala comunidade! Tudo certo com vocês?

    Para quem não me conhece eu sou a Karol Atekitta, Eu falo sobre tecnologia e ajudo a transformar vidas através da programação. Sou Senior iOS Engineer at Riot Games via Xteam, e hoje estou aqui para falar um pouco sobre Programação Orientada a Protocolos em Switf.

    Os protocolos são extremamente poderosos para garantir o desacoplamento, reaproveitamento, flexibilidade e a testabilidade do seu código. Sua utilização acabou ganhando uma aderência muito grande depois que o conceito de Programação Orientada a Protocolos foi apresentado por Dave Abrahams na Apple Worldwide Developers Conference (WWDC) de 2015. 

    Esse novo paradigma veio para resolver alguns problemas da Programação Orientada a Objetos, como por exemplo o uso exagerado de subclasses sem o conhecimento das consequências da herança dessas classes. À medida que aumentamos a complexidade do software, era muito comum cairmos em situações de efeitos colaterais onde alterações na classe herdada poderiam resultar em comportamentos inesperados nas subclasses. Com o uso dos protocolos, além de poder resolver essa questão da herança, eliminamos também a dependência entre classes, aumentando o nível de abstração.

    No entanto, seu uso exagerado até levantou questionamentos na comunidade, como sugere Chris Eidhof em seu artigo "Protocol Oriented Programming is Not a Silver Bullet", que acaba sendo bem sensato nesse ponto. A tentativa de abstração extrema pode nos levar a um código que apesar de flexível seja extremamente complexo e nem sempre aumentar a complexidade nos trará um ganho compatível. De fato não existe bala de prata, porém vamos entender a seguir como esse recurso pode te ajudar a aumentar a qualidade e flexibilidade do seu código.

    Segundo a documentação oficial da linguagem Swift:

     "Um protocolo define um esquema de métodos, propriedades e outros requisitos que atendem a uma determinada tarefa ou parte de uma funcionalidade. O protocolo pode, então, ser adotado por uma classe, struct ou enum para fornecer uma implementação real desses requisitos. Qualquer tipo que satisfaça os requisitos de um protocolo é considerado em conformidade com o mesmo."

    Em outras palavras, um protocolo é basicamente um contrato, onde estabeleço requisitos que devem ser implementados pelas representações que adotem esse protocolo, seja uma classe ou uma struct. Com esse contrato temos a garantia de que qualquer objeto que esteja em conformidade com o mesmo, implemente as propriedades e métodos definidos como requisitos.

    protocol MusicalInstrument {
      let name: String { get set }
      func makeSound() -> String?
    }
    

    Nesse exemplo acima temos um protocolo que define os requisitos de um instrumento musical, que deve possuir nome e também implementar a função de emitir o som '’makeSound". É legal entender que o protocolo é uma concepção abstrata, então ele define as regras mas não as implementa. Baseado nesses requisitos podemos criar diversas classes que adotem esse mesmo protocolo.

    struct Drum: MusicalInstrument {
      let name: String
      func makeSound() -> String? {
          return "Tu dum tatz!"
      }
    }
    
    struct Guitar: MusicalInstrument {
      let name: String
      func makeSound() -> String? {
          return "Blen blen!"
      }
    }
    

    Imagine que tenhamos uma classe que representasse um músico e ele tivesse a habilidade de tocar instrumentos. Inicialmente ele poderia tocar bateria e guitarra, porém outros instrumentos poderiam ser adicionados no futuro.

    class Musician {
      func playDrum(drum: Drum) { 
        drum.makeSound()
      }
      func playGuitar(guitar: Guitar) { 
        guitar.makeSound()
      }
    }
    

    Através do uso de protocolos poderíamos criar apenas um método genérico que aceitaria qualquer instrumento, afinal o protocolo nos garante um contrato de requisitos que deve ser seguido e por isso sabemos que qualquer objeto que adote esse protocolo, irá implementar adequadamente o método '’makeSound'.

    class Musician {
      func play(instrument: MusicalInstrument) { 
         instrument.makeSound()
         print("Playing \(instrument.name)")
      }
    }
    

    A mágica aqui está na questão do músico em si não precisar saber que tipo de objeto está lidando, ou seja, ele não depende de uma implementação concreta mas sim de uma definição abstrata. Não importa qual tipo de classe seja passada em seu método "play desde que ela adote o protocolo de instrumento musical, cumprindo seus requisitos.

    É possível que um objeto se conforme com vários protocolos e dessa forma podemos também segregar os comportamentos, algo que não poderíamos fazer com herança de classes. 

    protocol MusicalInstrument {
      let name: String { get set }
      func makeSound() -> String?
    }
    
    protocol EletricObject {
      var isOn: Bool { get set }
      func plugBattery()-> Void
    }
    
    struct Guitar: MusicalInstrument, EletricObject {
      let name: String
      var isOn: Bool = false
    
      func plugBattery(){
        isOn = true
      }
    
      func makeSound() -> String? {
     guard isOn else { return nil }
          return "Blem blem!"
      }
    }
    

    No exemplo acima, criamos um novo protocolo para objetos elétricos onde teremos como requisito a implementação de uma flag para verificar se o objeto está conectado à bateria. Adicionamos então uma nova lógica na função "makeSound" para saber se o instrumento está ligado ou não, e caso não esteja, não retornaremos nenhum som emitido.

    Essa possibilidade de segregar esses comportamentos criando protocolos específicos, vai ao encontro de um princípio importante do SOLID, o princípio da segregação de interfaces. Este princípio diz que as interfaces, ou protocolos, devem ser pequenas e específicas, e que cada classe deve implementar apenas as interfaces que ela precisa. Ou seja, é melhor ter várias interfaces pequenas do que uma única interface grande. Se colocássemos a flag 'isOn' em nosso protocolo de instrumento musical, os instrumentos que não fossem elétricos implementariam uma flag que não seria utilizada.

    A Programação Orientada a Protocolos é um paradigma de desenvolvimento de software que permite a criação de abstrações de comportamentos esperados de um determinado tipo de objeto. A vantagem desse paradigma é que ele permite que você crie código genérico que pode ser aplicado a muitos tipos diferentes, desde que esses tipos sejam conformes ao protocolo. Isso significa que você pode escrever código que trabalhe com qualquer tipo que satisfaça as especificações do protocolo, sem precisar conhecer o tipo concreto ao escrever o código e essa é base quando pensamos em criar implementações com um maior nível de abstração.

    Essa abstração acaba sendo fundamental na implementação de testes, pois isso permite que você crie, por exemplo, objetos que vão simular os comportamentos esperados, sendo possível testar partes isoladas do seu código sem necessitar da implementação concreta das suas dependências. Se quisessemos testar a nossa classe que representa o músico para saber se estamos tocando o instrumento devidamente, a partir do protocolo, poderíamos criar uma nova implementação que registra se o instrumento foi tocado ou não através de uma flag.

    struct MusicalInstrumentMock: MusicalInstrument {
      let name: String
      
      var makeSoundWasCalled: Bool = false
      func makeSound() -> String? {
          makeSoundWasCalled = true
          return ""
      }
    }
    

    No exemplo acima, sabemos que o instrumento foi tocado pois estamos alterando a flag "makeSoundWasCalled" sempre que o método "makeSound" for chamado.

    func testPlayInstrument() {
      let musician = Musician()
      let instrument = MusicalInstrumentMock()
    
      musician.play(instrument)
    
      XCTAssertTrue(instrument.makeSoundWasCalled)
    }
    

    Em nosso caso de teste então poderemos verificar pela flag, para saber se ao chamar o método play do nosso músico, ele irá corretamente chamar o método "makeSound" do nosso instrumento. Ao escrever testes com protocolo, você pode usar tipos fictícios para testar o comportamento esperado do código. Dessa forma, você pode garantir que o seu código está funcionando corretamente, independentemente de como ele é implementado. Por esse motivo a abstração de um código é tão importante para a qualidade do mesmo.

    Isso também permite com que possamos implementar um outro conceito muito importante do SOLID que é a inversão de dependência. Este princípio diz que as classes devem depender de abstrações, e não de implementações concretas. Isso significa que as classes devem ser projetadas de forma a serem independentes das implementações concretas de outras classes, e justamente através dos protocolos conseguimos chegar a esse nível de abstração.

    O Swift é uma linguagem de programação que oferece suporte a vários paradigmas: Programação Orientada a Objetos, Programação Orientada a Protocolos e Programação Funcional. Isso nos permite ter justamente a liberdade para escolher o paradigma que melhor se adequa às nossas necessidades. Além disso, é possível misturar e combinar diferentes paradigmas para obter soluções ainda mais flexíveis e adaptáveis.

    Compreender a Programação Orientada a Protocolos, é a base para o desenvolvimento iOS. Isso porque a Programação Orientada a Protocolos é amplamente utilizada nos próprios frameworks nativos da plataforma, e ter conhecimento aprofundado sobre esse conceito certamente ajudará a se aprimorar e evoluir como desenvolvedor iOS.

    Compartilhe
    Comentários (23)
    Luis Zancanela
    Luis Zancanela - 10/03/2023 11:43

    Este artigo tem utilidade dupla. Primeiro o conteúdo, não conhecia esse paradigma de programação orientada à protocolo. E segundo, ver que a Karol Attekita está compartilhando aqui, foi uma ótima surpresa que com certeza agrega demais para a comunidade, que venham mais artigos.

    MS

    Mateus Santos - 10/03/2023 21:35

    Caramba, a Attekita aqui. Que issoooo!!!

    Rebecca Machado
    Rebecca Machado - 10/03/2023 20:44

    Que top ter você por aqui também!

    ÍS

    Ícaro Santos - 10/03/2023 20:02

    É ótimo entrar nessa plataforma e saber que tem artigos de alguém que admiro como a Attekita.

    Sergio Paulo
    Sergio Paulo - 10/03/2023 18:02

    Muito legal, eu lembro de quando eu aprendi, más é sempre bom dar uma revisada.

    Maurício Silva
    Maurício Silva - 10/03/2023 11:57

    Uau, a Karol com um artigo top por aqui! Excelente

    Denilson Palma
    Denilson Palma - 10/03/2023 12:04

    Genial. Te acompanho no YouTube, obrigado por este conteúdo e o canal.

    RV

    Ricardo Vidal - 10/03/2023 12:19

    Parabéns, enriquecedor, pertinente e oportuno conteúdo.

    André Barbosa
    André Barbosa - 10/03/2023 16:22

    Uma referência da programação deu a maior empolgação agora

    Elaine Oliveira
    Elaine Oliveira - 10/03/2023 11:05

    grata pelo conteúdo!!!

    Samuel Edson
    Samuel Edson - 10/03/2023 15:33

    Não conhecia esse paradigma, pois não trabalho com swift. É bem semelhante com a implementação de interfaces. A diferença, que inclusive sinto falta nas outras linguagens onde é possível ter interfaces é a possibilidade de uma interface também extender outras interfaces e até mesmo implementar interfaces além das classes. Parabéns pelo artigo.

    SF

    Samuel Filho - 10/03/2023 13:15

    Conteúdo de elite 👏👏

    Wandson Santos
    Wandson Santos - 10/03/2023 00:11

    Nossa, a Atekitta aqui!! :o

    Ana Lopes
    Ana Lopes - 09/03/2023 20:35

    Bacana ver a Atekitta por aqui.

    Só que fiquei com uma dúvida: qual a diferença entre um protocolo e uma classe abstrata? Até onde entendi, são praticamente iguais...

    Leandro Santos
    Leandro Santos - 09/03/2023 19:09

    TOP Karol Atekitta e garantia de conteúdo de alto nível

    Kleiton Silva
    Kleiton Silva - 09/03/2023 18:11

    Parabéns Atekita pelo texto e a DIO pela parceria de muito bom gosto!

    Atekita, acompanho o seu canal a um tempo e mesmo nunca me interessando muito pelo desenvolviemtno de APPs em IOS sempre fico interessado vendo um video seu ou lendo um texto como esse. Se acabar me tornando desenvolvedor mobile em IOS será por influência sua XD.

    Sei que tem conteúdo técnico na internet sobre programação orientada a protocolos, mas qual ou quais materiais mais teóricos você recomendaria sobre o assunto para quem quer entender de verdade sobre este paradigma?


    Tennison Capra
    Tennison Capra - 09/03/2023 17:17
    Whaaat? Atekitta aqui na DIO? Que crossover dahora!

    GP

    Gilmar Pires - 09/03/2023 17:35

    Poxa que bacana ...que artigo maravilhoso..

    Leonardo Almeida
    Leonardo Almeida - 09/03/2023 17:31

    Isso aqui é a elite rsrs

    AJ

    Alvaro Junior - 09/03/2023 16:49

    Excelente 👏👏👏

    Giancarlo Rodrigues
    Giancarlo Rodrigues - 09/03/2023 17:04

    Grande Karol Atekitta!! Seja bem vinda!! : )

    Walter Pinto
    Walter Pinto - 09/03/2023 16:31

    Muito bom entrar na DIO e dar de cara com um artigo da Atekitta.

    Sempre um conteúdo de valor.

    Beatriz Silva
    Beatriz Silva - 09/03/2023 16:50

    Obrigada Dio .

    Conteúdos de qualidade. Estou aprendendo muito