Desmistificando a programação orientada a protocolos

Marcos Ferreira
8 min readJul 1, 2022

--

Muito se ouve falar sobre este tema, e muitos mitos são construídos sobre. Será que realmente a programação orientada a protocolos é o bicho de 7 cabeças que ouvimos dizer por aí? Embarque comigo nessa jornada para desvendarmos esse mistério.

Materials of detective
Photo by Mediamodifier on Unsplash

Para iniciarmos nossa jornada, vamos responder algumas questões e ao final deste artigo espero ter lhe ajudado a compreender este poderoso conceito.

  • Quando surgiu pela primeira vez?

O conceito de programação orientada a protocolos, ou simplesmente POP, foi originalmente apresentado na WWDC de 2015 por Dave Abrahams, trazendo uma nova proposta de desenvolvimento, proposta esta, que movimentou ativamente a comunidade com diversas discussões sobre o tema.

  • Entendendo alguns conceitos básicos da POP

Antes demais nada é preciso entender o conceito de um protocolo para entender como a POP funciona, dito isto, vamos pegar como referência o que a documentação oficial da Apple nos apresenta como definição de um protocolo.

Um protocolo define um modelo de métodos, propriedades e outros requisitos que se adequam a uma determinada tarefa ou funcionalidade. Ainda diz-se que qualquer tipo que satisfaça os requisitos de um protocolo está em conformidade com o mesmo.

  • Sintaxe de um protocolo

Um protocolo define um "contrato" que irá dispor suas "cláusulas" e seus "termos" afim de definir um determinado padrão para uma dada situação. Dito isto, esta seria uma forma de declararmos um protocolo:

No exemplo acima, apresentamos a estrutura base de um protocolo "QualquerCoisa" e este por sua vez declara uma função que "fazAlgo", realizando uma analogia direta, nosso "contrato" seria o "QualquerCoisa" e nossas "cláusulas e termos" seriam nosso "fazAlgo".

  • Explorando o máximo de seus protocolos

Com os protocolos podemos fazer muito mais do que apenas definir padrões, podemos definir como determinadas coisas vão se comportar sem a necessidade de pedirmos a quem utiliza o protocolo responder a esta pergunta.

Para isso usamos as extensions, através delas podemos prover implementações default para nossos métodos e/ou atributos, veja um exemplo abaixo:

No exemplo acima, definimos um protocolo Animal que possuí uma propriedade booleana que diz se um animal qualquer pode voar. Logo abaixo definimos uma extensão deste protocolo e adicionamos um "comportamento padrão" dizendo que, toda vez que perguntarmos se um dado animal voa, ele vai responder que sim (true) se a classe que esta conformando com o protocolo Animal também conformar com o protocolo Flyabe e que não (false) se a classe não conformar com Flyabe.

Apesar deste exemplo ser bem simples, conseguimos ter uma noção do poder que esta em nossas mãos. Ao se utilizar deste recurso é possível uma padronização/organização e até mesmo "automação" de determinadas tarefas rotineiras durante o desenvolvimento.

  • Porque usar o POP?
Please, tell me more!

Vamos analisar os principais pontos positivos que nos são apresentados para adotarmos a POP em nossos projetos:

1º) Quando utilizamos a POP conseguimos encapsular o conceito funcional de uma maneira que não requer uma classe base, tornando assim, o código muito mais flexível permitindo que um comportamento seja adotado em qualquer lugar. Se criarmos um paradoxo com a OO (orientação a objetos) veremos que ela nos "obriga" a criar uma classe base para propagar determinados comportamentos/caractéristicas para outras instâncias, conceito este, também conhecido, como herança.

2º) Outra vantagem na utilização da POP é podermos levar comportamentos encapsulados nos protocolos para instâncias de Structs e também Enums, além é claro das próprias Classes. Se optarmos por utilizar a orientação a objetos, a herança, é restrita apenas as classes.

3º) Um tipo pode estar em conformidade com múltiplos protocolos ao mesmo tempo, ao contrário de algumas linguagens que não permitem o conceito de herança múltipla.

4º) Um protocolo pode estar em conformidade com outro protocolo e através de suas extensões, definir comportamentos padrões para todos os elementos de todos os protocolos que ele conforma, além de si próprio.

5º) Quando trabalhamos com subclasses, as classes-filhas herdam TODAS as propriedades e métodos da classe mãe, e muitas das vezes, não existe uma real necessidade para isso. Com os protocolos temos a liberdade de utilizar apenas o que queremos. Dessa forma, nosso código fica mais enxuto e coeso.

6º) Outra vantagem da POP em relação as subclasses, é sua facilidade em localizar bugs e/ou editar comportamentos, enquanto que, ao se utilizar, super classes a navegação entre as classes da herança se torna complexa e o entendimento de seus relacionamentos nem sempre é claro, o que torna sua manutenção/evolução bem mais complicada e morosa.

7º) Na POP é recomendável a utilização de Value Types (Structs) e não mais Reference Types (Classes), incentivando assim, códigos mais curtos e não aninhados, mas isso não quer dizer que não é possível utilizar a POP com Reference Types.

8º) Prefira sempre utilizar abstrações ao invés de implementações concretas. Acredito que esta seja a dica mais valiosa de todas, quando você orienta seu desenvolvimento para abstrações (protocolos), as evoluções/mudanças tendem a ser muito menos dolorosas, uma vez que, basta um ajuste em seu "contrato" (protocolo) e uma adequação de todos que conformem com ele (seja através de uma implementação default ou não) e sua jornada continuará sem maiores problemas. Agora tente resolver o mesmo problema acima, utilizando implementações concretas de classes diferentes que tem um problema em comum, serão vários e vários commits em arquivos e locais diferentes ajustando um mesmo "problema".

  • Porque devemos criticar o uso da POP?
Photo by Minator Yang on Unsplash

Sim, nem tudo são flores, hoje em dia no meio da comunidade de desenvolvimento Swift, a programação orientada a protocolos está na "moda", existem várias bibliotecas open source, inclusive, que afirmam categoricamente que a POP é um "recurso".

Na relis opinião daquele que vos escreve, os protocolos são sim muito usados em Swift e muitas das vezes, o problema ao qual se quer resolver, poderia ser sanado de uma forma "muito mais simples". Poderia ser usada uma "simples Closure", ou até mesmo uma "Notification", não me interprete errado, não estou afirmando que usar ou deixar de usar protocolos é ou não é uma boa opção, e tampouco, menosprezo o seu poder.

Apresento aqui uma outra situação, que poderia, ser considerada uma "desvantagem". Qualquer funcionalidade definida para um dado tipo, como por exemplo, o método dropFirst, associado ao tipo Collections, estará presente em TODOS os tipos Collection "de graça". Ao mesmo tempo, existem tantos protocolos e tipos co-relacionados à Collections que pode ser "difícil" encontrar as coisas.

Uma heurística muito útil para determinar se à aplicação de protocolos é ou não uma boa saída, seria pensar, se o seu protocolo modelará dados ou comportamentos. Se for dados, talvez, uma Struct provavelmente seria uma melhor opção. Em contra-partida, se for comportamentos (complexos ou não), como por exemplo, o delegate de uma UITableView, um protocolo geralmente é o mais indicado.

Os protocolos de coleta que estão presentes na grande maioria das bibliotecas padrão da linguagem, são "especiais", eles realmente não descrevem dados (o que poderia gerar um certo "conflito" com o conceito apresentado anteriormente), mas eles descrevem como os dados coletados são manipulados.

  • Um aviso importante

Apesar da POP ser muito poderosa e simples de ser adaptada para as mais diferentes realidades ela NÃO é uma bala de prata, assim como qualquer outra abordagem em desenvolvimento de software, possuí pontos positivos e negativos e cabe exclusivamente a você adota-lá ou não ela. Tendo sempre um olhar menos dogmático quanto possível sobre o tema.

Agora que já entendemos os conceitos e conhecemos um pouco sobre a POP, é hora de por a mão na massa (quero dizer no código 🤣).

Photo by Clément Hélardot on Unsplash

Para este exemplo, vamos definir um problema simples e corriqueiro que com certeza você já enfrentou ao longo da sua jornada.

Realizar o parse de um JSON retornado por algum serviço!

Vamos tomar por base a seguinte classe que define algumas operações possíveis de nossa aplicação:

O código acima funcionaria perfeitamente, e não teria nenhum problema, até o momento em que se quisesse testar de forma isolada os métodos getCurrentUser() e getLastPayment(). Poderíamos criar um protocolo para que o URLSession se conformasse por meio de uma extension para tratar cada caso. E dessa forma, faríamos uma injeção de dependência passando uma instância de teste para cada cenário.

Bem moroso, concorda?! Entretanto, nem tudo esta perdido, existe uma solução bem mais simples do que à apresentada. Que consistiria em isolar as partes "mutáveis" de nosso exemplo em uma nova estrutura (struct ).

Ficando da seguinte maneira:

Tendo nossa nova estrutura definida, vamos remodelar nossa classe Services:

Bem mais enxuta e elegante, não acha?!

Com essa abordagem, agora não precisamos mais de 2 métodos para carregar diferentes recursos e com isso só precisamos testar o método de load. Entretanto, ainda podemos melhorar um pouco mais essa implementação. Ao invés de termos um método para realizar o parse, podemos criar um protocolo (olha o poder da POP entrando em ação novamente), que terá um inicializador que receberá um json como parâmetro para realizar essa tarefa, afim de separarmos ainda mais as responsabilidades.

Muito bom, agora que criamos mais um protocolo para nos auxiliar, chegou o momento de atualizar novamente nossa estrutura de representação para que ela esteja em conformidade com o novo protocolo. Ficando da seguinte forma:

E também não podemos nos esquecer de nossa classe Services:

Dessa forma nos utilizando dos poderes da orientação a protocolos conseguimos desacoplar as responsabilidades, tornando ainda mais simples nosso código, facilitando na hora de realizar os testes unitários, e consequentemente mantendo nosso código mais coeso e respeitando as boas práticas de programação.

  • Mais sobre POP

Gostaria de deixar 2 links bastante interessantes que podem lhe ajudar não só na sua jornada de entendimento/utilização da POP, como também, ajudar a tornar seus projetos ainda mais robustos e bem estruturados.

— Swinject: Uma excelente biblioteca para injeção de dependências, que permite que você explore novos horizontes, tornando fácil, a forma para registrar suas instâncias de classes concretas e expondo uma forma prática e fácil de recupera-las através de um protocolo (ou não).

— Property Wrappers: Este segundo link é de um excelente artigo que ensina como criar suas Property Wrappers (famosas "anotações" @ que temos em algumas linguanges). Te desafio a construir uma ferramenta que lhe auxilie no seu dia a dia para manipular seus protocolos usando essa "8ª maravilha do mundo que a Apple nos deu".

https://www.swiftbysundell.com/articles/property-wrappers-in-swift/

Gostaria de agradecer pela paciência se você chegou até aqui, e espero que tenha gostado do material. Caso tenha ficado com alguma dúvida não deixe de comentar na publicação, será um prazer ajudar a esclarecer qualquer dúvida.

Sugestões de temas para próximos artigos também são muito bem vindas, basta comentar o tema (ou me mande uma DM no Twitter 😎) que acha interessante que eu farei o possível para escrever algo sobre.

Até mais!!!

--

--