Design Patterns

Funcionais




Rodolfo Ferreira

rodolfoferreira.com.br

rodolfo42

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