Design Patterns
Funcionais
Rodolfo Ferreira
rodolfoferreira.com.brrodolfo42
Live
http://goo.gl/3XjEUx
Conteúdo
Conceitos-chave do Scala
Migração Strategy de Java para Scala
Filter - Map - Reduce
Conceitos-chave
Princípios
Funcional
Composição de funções, não classes
Valores, não instâncias
Declarativo, não imperativo
Scala
Linguagem híbrida (OO e funcional)
Tipagem estática
Código conciso
Funções: cidadãs nativas
def soma(a: Int, b: Int) = a + b
soma(1, 1) // 2
Inferência
de tipos
Funções anônimas
(a: Tipo, b: Tipo) => <corpo>
No caso do soma
anterior
(a: Int, b: Int) => a + b
Variáveis
final
val numero = 42
val nome: String = "Almeida"
substituíveis
var numero = 426
numero = 1004 // pode
Expressões
Tudo é uma expressão
(a: Int) => if(a == 0) "Zero" else "Não-zero"
Último valor é sempre o retorno
(a: Int) => { val res: Int = a * 5 res // ← retorno }
Funções de ordem superior
recebem e/ou retornam outras funções
def fazAlgoCom(numero: Int, algo: Int => Int) = algo(numero)
fazAlgoCom
é uma função de ordem superior
Int
=> Int
função que recebe um Int e retorna um Int
Funções de ordem superior
Exemplo de uso
def somaComDois(a: Int) = a + 2
fazAlgoCom(1, somaComDois) // 3
Operadores
1 + 2 → 1.+(2)
Apenas métodos com syntactic sugar
class Int { def +(x: Int) = ... }Faz uma diferença..
Operadores
Exemplo de uso
class Pessoa(val esporte: String) { def pratica(algo: String) = algo == esporte }
val rodolfo = new Pessoa("Skydiving")
rodolfo pratica "Skydiving" // true
Case classes
Para encapsular dados
Sintaxe concisa
case class Carro(val placa: String)
Não precisam de
new
val meuCarro = Carro("XYZ-1234")
A evolução dos seus POJOs
Case classes
Quando você faz isso
case class Carro(val placa: String)
Em Java seria isso
public class Carro { private final String placa; public Carro(String placa) { this.placa = placa; } public String placa() { return placa; } }
Case classes
Podem também ter funções próprias
case class Carro(val placa: String) { def prefixo = placa.substring(0, 3) }
val meuCarro = Carro("RAF-1234")
meuCarro.placa // "RAF-1234" meuCarro.prefixo // "RAF"
Imutabilidade
val umAteQuatro = List(1, 2, 3, 4)
umAteQuatro
sempre será o mesmo
val umAteCinco = umAteQuatro :+ 5
// List(1, 2, 3, 4, 5)
Strategy
Migrando para Scala
Propósito
Validar uma lista de pessoas
POJO →
Pessoa
Strategy →
ValidacaoStrategy
Collector →
PessoaCollector
Collector
irá colecionar objetos Pessoa
válidos
Java
Pessoa
public class Pessoa { private final String nome; private final String sobreNome;
public Pessoa(String nome, String sobreNome) { this.nome = nome; this.sobreNome = sobreNome; }
public String getNome() { return nome; } public String getSobreNome() { return sobreNome; } }
Strategy
public interface ValidacaoStrategy { public boolean valida(Pessoa pessoa); }
public class ValidacaoPorNome implements ValidacaoStrategy { @Override public boolean valida(Pessoa pessoa) { return pessoa.getNome() != null && pessoa.getNome().length() > 0; } }
Collector
public class PessoaCollector { private ValidacaoStrategy validacaoStrategy; private List<Pessoa> pessoasValidas; public PessoaCollector(ValidacaoStrategy validacaoStrategy) { this.validacaoStrategy = validacaoStrategy; this.pessoasValidas = new ArrayList<Pessoa>(); } public void add(Pessoa pessoa) { if (validacaoStrategy.valida(pessoa)) pessoasValidas.add(pessoa); } public List<Pessoa> getPessoasValidas() { return pessoasValidas; } }
Uso
PessoaCollector collector = new PessoaCollector(new ValidacaoPorNome());
Pessoa p1 = new Pessoa("Rodolfo", "Ferreira"); Pessoa p2 = new Pessoa("", "Quevedo");
collector.add(p1); collector.add(p2);
collector.getPessoasValidas().size() == 1
Extreme makeover
Pessoa
case class Pessoa( nome: String, sobrenome: String )
Validator
type PessoaValidator = Pessoa => Boolean
val nomeValidator: PessoaValidator = _.nome.length > 0
val nomeInteiroValidator: PessoaValidator =
_.nome.length > 0
&& _.sobrenome.length > 0
Collector
case class PessoaCollector(valida: ValidacaoStrategy) { val pessoasValidas = ArrayBuffer[Pessoa]()
def add(pessoa: Pessoa) = if(valida(pessoa)) pessoasValidas += pessoa }
Uso
val collector = PessoaCollector(nomeValidator)
val p1 = Pessoa("Rodolfo", "Ferreira") val p2 = Pessoa("", "Quevedo")
collector add p1 collector add p2
collector.pessoasValidas.length == 1
Conceitos aplicados
Composição de funções, não classes
Faz mais sentido e resulta em menos código
Case class
A melhor abstração para vários casos
Funções como peças de um lego
Resolver um problema aos poucos
Filter, Map e Reduce
Iteração
Como fazemos hoje
mais linguagem de máquina
menos vocabulário humano
Seu chefe
Sobre um carrinho de compras
"Desconta 10% dos produtos acima de R$20"
"Mostra o total do carrinho e o total do desconto"
Você
Implementando
Double valorTotal = 0d; Double descontoTotal = 0d; for (Produto produto : produtos) { valorTotal += produto.getValor(); if (produto.getValor() > 20.00) { descontoTotal += produto.getValor() * 0.1; } } valorTotal -= descontoTotal;
Funcional
Semântica ao resgate
val valores = produtos map (_.valor)
val valorTotal = valores reduce ((total, valor) => total + valor) val descontoTotal = valores filter (_ > 20.00) map (_ * 0.10) reduce ((total, desconto) => total + desconto)
val total = valorTotal - descontoTotal
Conclusões
Programação funcional =
composição
concisão
diversão
Scala =
tudo isso
JVM
Pense nisso
O inimigo central da confiabilidade é a complexidade.
- Daniel Geer
Agradecimentos
Michelle
Os caras da Bluesoft
Eicon
JCranky e Jonas Abreu
Vocês
Objects as functions
Todo objeto pode ser uma função
Quando houver um método apply
object FabricaDeCarros { def apply(placa: String): Carro = new Carro(placa) }
val novoCarro = FabricaDeCarros("XYZ-1234")
Functional Design Patterns
By Rodolfo Ferreira
Functional Design Patterns
- 1,653