Design Patterns em Scala – Parte 3: Pimp My Library


Nas duas primeiras partes da série Design Patterns em Scala, vimos alguns patterns bem conhecidos, porém implementados de uma maneira um pouco diferente em Scala.

Agora veremos um pattern que não é conhecido no mainstream Java, porque usa alguns recursos exclusivos da linguagem Scala, no caso de hoje esse recurso é a conversão implícita.

Estamos falando do Pimp My Library! Yooo!

Motivação

Se você leu o post sobre conversões implícitas, deve se lembrar do exemplo abaixo:

scala> val str = "Hello"
str: java.lang.String = Hello

scala> str.toList
res0: List[Char] = List(H, e, l, l, o)

E agora, com toda a mágica desmistificada, você deve saber que está ocorrendo uma conversão implícita de String para alguma classe que contenha o método .toList().

Mas sem pensar no que está acontecendo por baixo dos panos, qual a sensação que temos quando escrevemos o código acima? Qual a percepção do desenvolvedor?

Parece que foram acrescentados métodos diretamente na classe final String, não é? Se observarmos o código, não dá nem para saber qual a classe que efetivamente define o método .toList().

Esse exemplo é exatamente uma aplicação do pattern Pimp My Library! A classe String foi… éé… “Pimpada”!

Ok, “incrementada”, “aumentada”, “melhorada”, são termos melhores… Mas enfim…

O Pimp My Library é útil para os casos em que você usa uma classe ou API que não é sua e deseja estendê-la, de maneira transparente, para melhor se adaptar às suas necessidades.

Esse pattern foi definido (e nomeado) pelo próprio Martin Odersky, criador da linguagem, nesse artigo.

A primeira vista esse pattern se parece muito com Ruby Open Classes ou até mesmo Prototypes em Javascript, no sentido de que se consegue atingir o mesmo efeito.

No entanto, fazer Pimp My Library em Scala é algo muito mais controlado e localizado. Você não está efetivamente fazendo uma alteração na classe e afetando todo restante da sua aplicação (o que pode sair do controle e introduzir bugs estranhos).

É mais controlado fazer isso em Scala porque você precisa trazer explicitamente para o escopo do seu código o método implicit que faz as coisas acontecerem.

Vamos entender melhor como funciona…

Implementação

Agora que sabemos como funcionam as conversões implícitas fica fácil fazer o resto!

Tudo que precisamos é fazer um rich wrapper que possui os métodos que desejamos acrescentar a classe a ser incrementada.

Você adoraria que String tivesse um método inverte() que te devolve a String de trás para frente? E mais, você quer esse método em português?? Sem problemas, faça você mesmo:

// A primeira coisa que você precisa é de um wrapper bem legal
class StringBombada(str:String) { 
  def inverte():String = {
    val listaRev = str.toCharArray.foldLeft(new ListBuffer[Char]) { (lista, char) => char +: lista }
    listaRev.mkString  
  }
}

// A segunda coisa é uma conversão implicita no escopo. 
// Você pode deixar esse método dentro de um object.
implicit def stringParaStringBombada(str:String):StringBombada = {
  new StringBombada(str)
}

E a prova final:

scala> "socorram-me subi no onibus em marrocos".inverte
res17: String = socorram me subino on ibus em-marrocos

E é só isso. Lembrando que a conversão só acontecerá quando o implicit estiver no escopo.

Quick Pimp

Se você quer fazer uma coisa mais rápida, adicionar só um método aqui e ali, e acha que escrever um Wrapper é muito trabalhoso, apresento-lhe o Quick Pimp.

O Quick Pimp é uma versão light do Pimp My Library, que usa Structured Typing e Classes Anônimas ao invés de um Wrapper completo e dedicado:

implicit def string2paraInteiro(s: String): { def paraInteiro: Int } = new {
  def paraInteiro: Int = java.lang.Integer.parseInt(s)
 }
}

Veja, só com esse implicit no escopo eu consigo fazer isso:

scala> "12345" paraInteiro
res1:Int = 12345

Prático, conciso, elegante…

O QuickPimp se baseia em Structural Typing que internamente depende de reflection. Portanto, pode ter algum impacto em partes em que a performance é crítica.

Recapitulando

Nesse post começamos a entrar mais a fundo no mundo exclusivo de Scala, conhecendo um pattern muito usado, inclusive na própria API do Scala, chamado Pimp My Library, e sua variação light Quick Pimp.

Esse pattern lembra os open-classes e monkey patching do Ruby, mas de uma forma mais controlada e type-safe. É extremamente útil para incrementar classes e APIs existentes, mesmo que elas sejam de terceiros, e mesmo que elas sejam final.

E por hoje é só!

Anúncios