Concrete Logo
Hamburger button

Generics, o quebra galho para soluções genéricas

  • Blog
  • 25 de Junho de 2018

Enfrentamos um problema em projetos tanto pequenos, quanto grandes: o reuso de código, utilizado em inúmeros lugares. Ele faz com que não consigamos, às vezes, generalizar as nossas classes para centralizar em um único algoritmo, ação ou comportamento padrão. O polimorfismo paramétrico, mais conhecido como Generics, no Swift, nos quebra um grande galho na hora de pensar no design de classes e algoritmos.

Vamos tomar como exemplo a construção de um Data Source e Delegate de uma UIcollectionView. Vamos ver como, geralmente, parece o código que utilizamos para configurar uma Collection View no Swift e então vamos refatorar esse código, deixando-o genérico para ser reutilizável em qualquer Collection View do nosso projeto.

No exemplo de código acima podemos encontrar alguns problemas que impedem que o nosso Data Source seja reutilizado pra qualquer Collection View. Um desses exemplos é o array de beers, que está tipado para ser um array de BeerModel, fazendo com que qualquer outro tipo de objeto que porventura queiramos apresentar não seja suportado por esse Data Source.

O nosso dequeue de cell está com o Identifier da Cell, sendo buscado em uma constante e isso impede que qualquer outra cell possa ser instanciada por esse Data Source; temos ainda um delegate associado ao Data Source para nos informar qual foi a cerveja selecionada pelo usuário.

Todos esses pontos são pontos que geram acoplamento, tornando a classe com pouco reuso, difícil de testar, suscetível a mudanças de implementação influenciadas por mudanças a objetos externos acoplados a ele e assim por diante.

Vamos refatorar o nosso código e transformá-lo em um algoritmo genérico que vai poder ser reutilizado por qualquer Collection View que queira exibir algum objeto e, porventura, capturar o objeto selecionado.

A primeira coisa que faremos é adaptar as nossas células para que elas possam prover algumas informações; vamos criar também um protocolo que vai garantir que nossa cell receba um tipo e saiba se configurar com ele:

Nós vamos precisar que a nossa célula também forneça qual vai ser seu Identifier, mas para isso temos que criar mais um protocolo chamado ConfigurableCell:

Vamos implementar esses protocolos em nossa Cell e conformar nossa cell com os protocolos que precisamos que ela implemente. Isso para que ela possa ser utilizada em qualquer Data Source que desejarmos:

Podemos observar que concretizamos o nosso associatedtype DataType como o tipo BeerModel, para que quando a célula for se configurar através da func setupCell(data: DataType), ela saiba exatamente o que esperar para apresentar em suas propriedades.

Observamos também que atribuímos valor à propriedade cellIdentifier: static let cellIdentifier = “\(self)”, nesse caso estamos retornando o próprio nome da classe como identifier da cell.

Tendo configurado a nossa Cell, vamos agora fazer as alterações no Data Source:

Em nosso refactor vamos apontar as principais mudanças e os impactos que isso traz para nós.

Primeiramente, podemos observar que a declaração do nosso Data Source mudou um pouquinho, já que adicionamos a ele a seguinte parte: <T: UICollectionViewCell & SetupableCell & ConfigurableCell>. Nesse caso, estamos fazendo uso do polimorfismo paramétrico, estamos informando para a nossa classe que vamos usar um tipo genérico T.

Mas não é só isso: estamos também assinando um contrato com a nossa classe, estabelecendo que T sempre vai ser um tipo que representa uma UICollectionViewCell, que implementa SetupableCell e ConfigurableCell, protocolos que implementamos anteriormente em nossa Cell que vai prover algumas informações para o nosso Data Source.

Já o nosso array de objetos, para ser apresentado agora, não é mais um array de beers com o tipo array de BeerModel, agora o nosso array se chama data e é um array do tipo T.DataType, ou seja, o tipo do modelo esperado pela nossa célula para se configurar.

Substituimos o nosso delegate, que informava o objeto selecionado da coleção por um callback, que vai fazer exatamente esse mesmo trabalho, nos devolvendo um objeto selecionado também do tipo que estamos trabalhando ali naquele momento.

Todas essas alterações proporcionam que o nosso Data Source possa apresentar qualquer célula de qualquer tipo de dados sejam eles quais for. Nosso Data Source é genérico o suficiente para prover essa interface de apresentação a qualquer célula que deseje apresentar algum conteúdo.

E para utilizar esse Data Source precisamos apenas concretizar T. Temos que passar uma cell que esteja em conforme com os protocolos SetupableCell e ConfigurableCell, para decidir o que fazer com o objeto selecionado. Como no código abaixo:

Espero ter ajudado vocês a entenderem um pouco mais sobre polimorfismo paramétrico, reuso de código e design de classes. Se quiser, deixe um comentário nos campos abaixo.

Grande abraço.

A Concrete é uma empresa da Accenture especializada no desenvolvimento ágil de produtos digitais. No capítulo de iOS temos a oportunidade de viver em uma comunidade prática com dezenas de desenvolvedores iOS, trocando conhecimento e compartilhando informações técnicas e práticas. Além de desafiadores, os projetos têm acompanhamento próximo dos gerentes, que estão sempre buscando entender o momento de cada membro do capítulo, considerando seus próximos desafios e oportunidades de crescimento e visando a melhoria contínua e evolução de todos. Práticas ágeis, lean, visão de produto e engenharia sólida são pilares que serão incorporados desde o primeiro dia até se tornarem quase que uma segunda natureza. Quer fazer parte disso? Acesse: concrete.com.br/vagas