Design, Design Patterns

Sua API fala? – Interfaces fluentes

Pra começo de conversa eu chego e digo logo que não gosto da forma como Java trata datas e calendários. Há alguns anos atrás eu lia alguns programadores ‘rock-stars’ dizerem que era uma API mal feita e tudo mais. Mas eu não entendia o porquê. Bom, há algum tempo eu tenho entendido a razão disso. Tirando o fato da classe Date e da classe Calendar serem mutáveis (inclusive essa questão já é clichê, falo disso em outro post – prometo), as operações não são nada elegantes. Trabalhosas, verbosas, exigem um monte de classes pra formatar tipo DateFormat, NumberFormat (no caso de números), apresenta problemas de performance em certos casos, difícil de estender e etc.  Todo mundo que já trabalhou um pouco com Java sabe como essa API funciona. Talvez não tenha noção do como ela é chata de trabalhar, mas sabe como funciona. Veja isso por exemplo:


//Isso tudo é pra setar uma data (12 de fevereiro de 1987)

 Calendar calendar = Calendar.getInstance();
 calendar.setTimeZone(TimeZone.getTimeZone("Universal");
 calendar.set(Calendar.DATE, 12);
 calendar.set(Calendar.MONTH, 2 - 1);
 calendar.set(Calendar.YEAR, 1987);
 Date feb12_1987 = calendar.getTime();

Um monte de linha de código pouquíssimo expressivas. Mais feio que parto de ouriço. E olhe que essa data não está sendo formatada. Se alguém quiser escrever isso no cabeçalho duma página, por exemplo? Mais trabalho ainda pra formatar em String e… haja paciência. Não estou querendo dizer que você jamais deva usar a API Dates e Calendars na vida. Mas, veja como essa API soa melhor:


//Setando a mesma data (12 de fevereiro de 1987) usando Joda-Time API

DateTime feb12_1987 = new DateTime().withDayOfMonth(12).withMonthOfYear(2).withYear(1987);

Então? Bem melhor, não? Não mudei o objeto em questão (cada método chamado retorna um novo objeto DateTime modificado), não usei variáveis static, não precisei criar um calendário pra fazer operações no meu tipo principal (no caso data) e o melhor de tudo: tudo em uma linha e facílimo de ler. Muito elegante. Esse é um belo exemplo onde a API Joda-Time usa method chaining, que é a técnica de encadear os métodos chamando-os em seguida, mas, vai além, faz o serviço de forma fluente! Repare como essa chamada parece mesmo uma frase: “nova data com dia do mês:12, com mês do ano:2 e com ano 1987.”  Alguém pode dizer que isto é, na verdade, o padrão Builder. Bom, também. Mas não apenas. Builder se preocupa apenas em construir objetos, não com legibilidade e fluência. Builder por padrão é usado com métodos set() e isso por si só já tira a fluência da chamada. Ah sim, além disso podemos fazer qualquer operação no objeto com interfaces fluentes (formatar saída, fazer conversões etc.), enquanto com Builder a idéia é apenas construir o objeto. Enfim, a API do Joda fornece uma porção de interfaces fluentes que me deixam feliz e satisfeito =D . Mas, o que exatamente é a tal da interface fluente?

Interface fluente é um estilo de escrever código no qual a função ou as funções que você deseja desempenhar são feitas de uma maneira bem mais simples do que a maneira usual e simplesmente “fluem”. “Interfaces” no sentido mais amplo da palavra , ou seja, os métodos expostos de uma dada classe. E não necessariamente interfaces Java.  Também podem ser chamadas de Interfaces Humanas, visto que o código cliente é que vai ser muito mais fácil e legível de escrever e compreender. Já pra quem escreve a API é mais difícil. Sim, mais difícil porque exige muito mais tempo pensando (às vezes muito tempo mesmo) em como prover um sistema de encadeamento de métodos que qualquer pessoa possa usar facilmente, com uma nomenclatura super clara que realmente indique qual o passo a seguir e ainda com todos os atributos de qualidade que uma boa API deve ter – alta coesão e baixo acoplamento.  Nem adianta eu ficar tentando explicar com conceitos esse estilo de API. Um exemplo vale mais do que mil palavras. E com certeza você vai entender melhor o que são de fato com exemplos. Suponha uma classe Money que representa uma quantia de dinheiro em qualquer moeda. Eu quero dar suporte aos clientes dessa interface para fazer soma de valores, subtrações de valores, conversão de moedas (tanto em símbolo que representa a moeda como em taxa de conversão) e formatação pra String. Então criei uma classe Money (me apeguei mais a fluência dela e deixei algumas potenciais falhas , por exemplo  tratamento de erros e outros, não reparem):

 


public class Money {

  public static final String REAIS = "R$";

  public static final String USDOLLARS = "U$";

  public static final String EUROS = "€";

  public static final String COMMA = ",";

  public static final String POINT = ".";

  private double value;

  private boolean usingComma;

  private static NumberFormat formatter;

  static {

    formatter = NumberFormat.getNumberInstance();

    formatter.setMinimumFractionDigits(2);

  }

  //Valor padrão

  private String currency = USDOLLARS;

  private Money(double value) {

    this.value = value;

    this.usingComma = false;

  }

  public static Money get(double value) {

    return new Money(value);

  }

  public static Money get(String value)  {

    double numValue = Double.parseDouble(value);

    return new Money(numValue);

  }

  public double value() {

    return this.value;

  }

  public long inCents() {

    return (long)this.value*100;

  }

  public String asString() {

    String formattedValue = formatValue();

    return formattedValue;

  }

  public Money in(String currentCurrency) {

    this.currency = currentCurrency;

    return this;

  }

  public Money usingComma() {

    this.usingComma = true;

    return this;

  }

  public Money convertTo(String currency) {

    this.value *= MoneyConversion.calculate(this.currency, currency);

    this.currency = currency;

    return this;

  }

  public Money plus(double amount) {

    this.value += amount;

    return this;

  }

  public Money minus(double amount) {

    this.value -= amount;

    return this;

  }

  private String formatValue() {

    String formattedValue = this.currency + formatter.format(this.value);

    if(this.usingComma) {

      if(formattedValue.contains(POINT)) {

        formattedValue = formattedValue.replace(POINT, COMMA);

      }

    }

    else {

      if(formattedValue.contains(COMMA)) {

        formattedValue = formattedValue.replace(COMMA, POINT);

      }

  }

  return formattedValue;

}

public static void main(String[] args) {

    String result1 = Money.get(10).in(REAIS).convertTo(USDOLLARS).usingComma().asString();

    System.out.println(result1);

    long result2 = Money.get(1).plus(15).plus(60).minus(50.5).inCents();

    System.out.println(result2 + " cents");

    String result3 = Money.get(0).plus(100).in(EUROS).asString();

    System.out.println(result3);

  }

}

 

“Ah!” – alguém pode dizer – “mas não era melhor fazer métodos do tipo…  new Money(10).convertFromReaisToDollarsUsingCommaAndFormatAsString ?” Veja bem, nesse caso o método viola o Princípio da Responsabilidade Única fazendo 3 coisas. Além disso, eu continuo a ter uma grande quantidade de métodos como seria no caso sem interface fluente. Imagina só fazer um método para cada combinação diferente de operações! Flexibilidade zero.

É meio estranho o fato de você retornar o próprio objeto num método modificador. Mas lembre-se que esse é um padrão de escrita diferente. Diferente de  JavaBeans, onde o objeto teria apenas seus atributos e cada um com seu get e set correspondente. Para obtermos a fluência precisamos invocar os métodos desejados sempre retornando algo que possa ser usado na próxima invocação. No caso, eu escolhi por retornar novas instâncias de Money(de forma que eles são imutáveis) exceto quando o cliente desejar pegar o resultado final das operações, quando o retorno é uma String com o valor formatado (asString()) ou a quantidade de centavos (caso queira formatar da sua própria forma). Mas como eu disse, Money é uma classe boba no sentido dos pequenos detalhes. A ênfase está nos nomes e eu não me canso de estressar esse ponto: Pare bastante pra pensar nos nomes dos métodos da sua interface fluente! Sem dúvida é bem mais trabalhoso fazer algo assim com qualidade, mas os resultados são fascinantes. Ah sim! Note que algumas linguagens tornam essa tarefa mais fácil, por exemplo Ruby e Python, tão falados hoje em dia. Em Java e C# já não é tão fácil ou os resultados não são ótimos. C++ e  C são verbosas demais, cheias de lixo sintático e tal. No mais, linguagens dinâmicas (também por serem ricas em syntax sugar) são mais produtivas pra isso também.

Mas cuidado! Não saia por aí fazendo interfaces fluentes em todo código que pegar. É uma técnica que deve ser usada com moderação e razoabilidade. Uma classe com 2 métodos e sem nenhuma chance de crescer não tem motivo de ser fluente – já é simples por natureza. Se não houver um bom range de combinações possíveis de operações também não compensa – muito trabalho pra projetar algo que não vai ser usado. E cuidado pra não ficar com mania de fluência e querer aplicar princípios de fluent interfaces em classes normais, fazendo sets retornar objetos e tal. Isso é uma violação do princípio CommandQuerySeparation. Simplesmente não funciona, ok?

Note que TDD (Test-Driven Development) cai como uma luva se você está querendo implementar Fluent Interfaces.  Tipo, primeiro você escreve a forma que o cliente deve chamar as operações (seu código não vai compilar) e depois escreva apenas código necessário para fazer seu teste compilar.  Depois refatore sua implementação para fazer o teste passar. Existe uma pá de bons artigos sobre interfaces fluentes. Eu gosto desse e desse principalmente que é baseado no post clássico do Martin Fowler. Outros exemplos de APIs Java escritas neste estilo e amplamente difundidas são JMock (para testes) e Criteria do Hibernate. Enfim, a idéia motivadora por trás disso tudo é sempre a mesma – programe não apenas para a máquina entender, mas [principalmente] para as pessoas entenderem.

Anúncios
Padrão
Design, Design Patterns, Lógica

Tua hierarquia não irá te salvar garoto – Strategy

Imagine que hoje seu chefe chegou inspirado e te deu a missão de desenvolver o protótipo de um jogo de esportes. Nesse jogo, existem vários jogadores de modalidades distintas. De forma que todos eles possuem capacidades físicas inerentes aos esportes que praticam. Tipo, um jogador de futebol consegue correr muito rápido e pular razoavelmente alto. Um jogador de vôlei, por outro lado, não corre tanto assim, mas pula muito alto. Você, como excelente desenvolvedor em Orientação a Objetos pensa: “Já matei! Mais um problema manjado de herança!”. Bom, não necessariamente está errado. Você provavelmente vai surgir com uma solução. Mas, é esta solução a melhor? É a mais flexível, mais fácil de testar e de melhor manutenção?

Vamos lá, a primeira coisa a se fazer se você quer criar uma hierarquia de tipos é criar uma classe ‘pai’. Para jogadores parece bem óbvio… hum… Jogador. Vamos assumir para fins didáticos que a classe Jogador tem apenas 2 métodos: correr() e pular(), ambos abstratos. Em seguida, bolamos as classes filhas, digamos: JogadorFutebol e JogadorHoquei. No caso, teríamos:

public abstract class Jogador {
     public abstract void correr();
     public abstract void pular();
}
public class JogadorFutebol extends Jogador {
   public void correr() {
       System.out.println("correr como jogador de futebol...");
   }
   public void pular() {
       System.out.println("Pular como jogador de futebol...");
   }
}

public class JogadorHoquei extends Jogador {
    public void correr() {
        System.out.println("Correr de patins...");
    }
public void pular() {
        System.out.println("Não pular...");
    }
}

Legal! Isso funciona! Então beleza, agora seu chefe chega e diz que seus clientes estão loucos ensandecidos para que o jogo dê suporte a tênis! Bom, agora você vai ter apenas que criar mais uma classe filha de Jogador, só que dessa vez o jogador corre muito e não pula (o cliente pediu isso especificamente =P). Daí, resolve criar a classe JogadorTenis, que… “peraí! Eu posso herdar o corre() do jogador de futebol e o pula do jogador de hóquei e vou economizar um monte de código!” Hehe, tá, mas existem alguns problemas aí. Muitas linguagens nem mesmo suportam herança múltipla! Outra, herança múltipla pode te trazer outros problemas seríssimos. “Ah, então eu escolho uma das duas pra herdar e o outro método eu copio. =]” Nossa! Eu não sei como eu penso nessas coisas… Mas é uma idéia terrível! Primeiro, você não deve herdar classes com o objetivo de economizar linhas de código! Isso escapa completamente o objetivo de OO. Você herda quando uma classe realmente têm um relacionamento “IS-A”. Ou seja,  quando ela for uma especialização da classe pai e esta for uma generalização das filhas. No caso, JogadorTenis não é um JogadorHoquei nem tampouco um JogadorFutebol. Esqueça economizar linhas de código.

Esse primeiro motivo deve bastar, mas eu ainda completo com o segundo. Você ainda assim estaria duplicando código de outras classes. Por exemplo, se você resolve criar uma classe JogadorTenis que ‘extends’ Jogador, e implementa o método correr() como escrito na classe JogadorFutebol e pular() como na classe JogadorHoquei você estaria duplicando código 2 vezes! E isso é ruim, muito ruim. Imagina se mais tarde é descoberto um erro no método da classe pai? “Copia e cola” seria o seu segundo nome… É… herança parece ser uma má idéia nesse caso aí. Outro problema: caso o jogador se machuque e não consiga mais, digamos, correr. Como fazer a instância de um jogador de futebol executar um correr() diferente dinamicamente? E agora? De fato, você ficaria sem saída. Você poderia fazer vários tipos de jogador de futebol representando os estados do seu JogadorFutebol, sendo que cada uma herdando a classe pai… não, essa não é uma solução elegante. A sua hierarquia iria aumentar, o que significa que a raiz do seu problema ia crescer e assim sucessivamente.  Imagina agora se surge um JogadorPoker. Mas enfim… a ideia está próxima! O que você acha de composição? Pense nos comportamentos que os jogadores podem ter relacionados com as suas ações. Por exemplo, correr como? Pular de que maneira? Correr rápido é uma forma de correr. Não correr poderia ser outra. Correr de patins seria outra… E pular? Pular alto como um jogador de vôlei seria uma forma. Pular baixo seria outra… já deu pra pegar a idéia. Então, podemos encapsular esses comportamentos visto que são eles que mudam (e não os jogadores como estávamos pensando no começo do problema!). Esses comportamentos serão uma hierarquia por si próprios. E a maldita da composição, onde entra na história? Os seus jogadores vão possuir(HAS-A) esses comportamentos. Mais explicitamente, no caso de correr:

public interface AcaoCorrer {
     public void correr();
}
public class CorrerComPatins implements AcaoCorrer {
     public void correr() {
         System.out.println("Correndo com patins...");
     }
}
public class CorrerRapido implements AcaoCorrer {
     public void correr() {
         System.out.println("Correndo rápido como um jogador de futebol...");
     }
}
public class NaoCorrer implements AcaoCorrer {   
     public void correr() {
         System.out.println("Não corre.");
     }
}

public abstract class Jogador {

     AcaoCorrer acaoCorrer;
     AcaoPular acaoPular;

     public Jogador() {
         this.acaoCorrer = new NaoCorrer();
         this.acaoPular = new NaoPular();
     }
     public void correr() {
         this.acaoCorrer.correr();
     }
     public void pular() {
         this.acaoPular.pular();
     }

     public abstract void exibir();
     public void setAcaoCorrer(AcaoCorrer acaoCorrer) {
         this.acaoCorrer = acaoCorrer;
     }
//Foi omitido, mas suponha que existe uma ampla variedade de formas de pular, sendo todas filhas de AcaoPular    
    public void setAcaoPular(AcaoPular acaoPular) {
         this.acaoPular = acaoPular;
    }
}


Funciona do mesmo jeitinho e com mais vantagens:

1 – Temos um design voltado a interfaces ao invés de classes. Favorece a flexibilidade. Quando fazemos um comportamento ser representado por uma interface, ao invés de ser implementado na classe Jogador ou em uma de suas subclasses, estamos de certa forma “liberando” esse comportamento pra fora da classe. O comportamento (ou a hierarquia de comportamentos) está fora da própria classe. Além disso, estamos nos referindo ao comportamento de forma generalista, no caso AcaoCorrer > CorrerComPatins por exemplo. E na implementação apenas mencionamos como atributo o tipo da interface AcaoCorrer e não as reais implementações.

2 – É menos complexo você compor uma classe com outra do que começar uma nova hierarquia. Você estará sujeito a menos erros bobos assim.  Ah sim! E hierarquias podem se tornar coisas do capeta depois de 3 níveis.

3 – Podemos mudar o comportamento dinamicamente ou “on the fly”. Com os métodos setter dos comportamentos, podemos mudar o tipo de comportamento que quisermos quando bem entendermos. Exemplo:


public class Jogo {

    public static void main(String[] args) {

//Um jogador de futebol corre muito e pula normalmente

        Jogador jogadorFutebol = new JogadorFutebol();

        jogadorFutebol.correr();

        jogadorFutebol.pular();

//Um jogador de hoquei corre de patins e não pula

         Jogador jogadorHoquei = new JogadorHoquei();

         jogadorHoquei.correr();

         jogadorHoquei.pular();

//Um jogador de hoquei machucado, não consegue correr nem pular

         jogadorHoquei.setAcaoCorrer(new NaoCorrer());

         jogadorHoquei.correr();

         jogadorHoquei.pular();

    }

}

Note que é possível setar os comportamentos de correr e pular porque o parâmetro esperado no método setter é uma generalização – uma interface. Qualquer classe que implemente a interface em questão (o que significa que esse comportamento saiba correr ou pular) poderá ser passada como novo comportamento do Jogador.  Caso surja um JogadorPoker, ele será composto por comportamentos já existentes, NaoCorrer e NaoPular. Logo, não haverá duplicação alguma de código.

Todos esses passos mostrados acima compõem o conhecido padrão de projeto Strategy. Esse padrão está catalogado como um dos 23 Design Patterns do “Gang of Four” ou “GoF”.  Da próxima vez que você se deparar com um problema que tenha essa natureza de encapsular vários algoritmos ou comportamentos num mesmo objeto, lembre-se do padrão Strategy, que pode “mudar as vísceras” de um objeto.  Calma! Não saia por aí dizendo que conheceu um jeito de modelar objetos que mudou a sua vida (principalmente se você descobriu isso aqui nesse blog, nesse caso pesquise mais um pouco =P ). Esta é uma solução para esse tipo de problema, NÃO PARA TODOS.  Por isso, use-a com equilíbrio.

Existem várias APIs famosas que usam Strategy, como JPA (que usa 3 algoritmos diferentes de mapear classes a tabelas do BD) e o componente JComponent (na escolha de Borders) do Swing.  Como eu falei, essa solução clássica resolve vários problemas, e se você for do tipo que se depara com muitos, Strategy vai ser algo comum na sua vida. Por isso, ao se deparar com um problema que parece ser facilmente resolvido estendendo classes e/ou criando uma nova hierarquia, pense se composição não seria uma melhor solução. Não apenas você ficará feliz com uma solução elegante e abrangente, mas os programadores que porventura pegarem esse código no futuro também ficarão. =]

Padrão
Experiências, Lógica

Quando a realidade esmaga a intuição

Suponha que você esteja num programa desses tipo porta dos desesperados. O apresentador lhe convida a participar do jogo que funciona da seguinte forma:

Existem 3 portas. As 3 estão fechadas de forma que você não consegue ver o que cada uma contém. Porém, uma delas esconde um carro como premiação e as outras duas um bode cada. A decisão de onde estará a porta com o carro é feita aleatoriamente. E depois de escolhida a porta, o prêmio é fixado a ela. Imagine que você (o participante) escolhe uma das 3 portas. Logo após você anunciar  sua escolha, o apresentador faz aquele suspense e abre uma das outras 2 portas, de forma que ele sempre deixa um bode sair. Agora você tem 2 portas: uma com o carro e uma com o bode. Agora o apresentador lhe faz a pergunta chave: “Você deseja trocar de porta ou continuar na mesma?” Qual seria a melhor decisão para você ganhar o carro? “Obviamente dá no mesmo. É  50% de chance de qualquer jeito…” Pois é. Fácil, né? NÃO! Por mais que a sua intuição clame desesperadamente para ser ouvida, a verdade é que, trocar de porta, nesse caso, dobra suas chances de vencer em relação a continuar com a primeira escolha. Mais especificamente, ao trocar de porta você tem 2/3 de chance de ganhar o carro.

 

The Monty Hall Problem


Esse problema,”The Monty Hall Problem”, ficou conhecido mundialmente após Marilyn Vos Savant publicá-lo em uma coluna sua numa revista de puzzles e matemática nos E.U.A. Os leitores da revista ao se depararem com o problema ficaram chocados com a resposta. E não eram pessoas do dia-a-dia ou alunos prevestibulandos, eram de fato Ph.Ds em matemática, doutores em estatísticas, cientistas renomados e etc. Estes enviaram cartas agressivas para a senhora Vos Savant em sua própria coluna, expondo o erro crasso que ela supostamente havia cometido. Eles argumentavam coisas do tipo

“Não vou nem mesmo perder muito tempo com a explicação do problema. Queira reconhecer seu erro e admitir que a resposta é 50% como nós dois bem sabemos…”

“Quer você mude sua resposta, quer não, a resposta é 50-50 de chance. Esse país já está cheio de ignorantes em matemática. Não precisamos do maior QI do mundo propagando isso. Que vergonha!”

Após algumas semanas de cartas e mais cartas, explicações e mais explicações, os matemáticos cederam e aceitaram a resposta de Vos Savant. “Quando a verdade se choca tão violentamente com a intuição” – disse ela – “as pessoas ficam chocadas.”

Paul Erdös (pronuncia-se ‘Erdish’), grande matemático húngaro, maior publicador de trabalhos na área, foi uma das pessoas que se chocaram com a resposta. E pior ainda: por não entender porque a resposta está certa. Aconselho esse livro, contando mais sobre essa e outras histórias.

 

O que danado isso tem a ver com programação ?


Bom, desafiei algumas pessoas no trabalho com esse problema a título de brincadeira. Como era de se esperar, todos responderam que a chance era 50-50 e protestaram veementemente quando eu falei que era muito melhor idéia trocar de porta. Experimente. As pessoas realmente enlouquecem com isso. É a intuição delas que está em jogo! Após muita discussão, foi feito um simulador em Java para provar que a chance era de 50%. Catch-22. O programa respondeu que era melhor trocar de porta, com aproximadamente 2/3 de chance de vencer ao fazer isso. Muito bem. A resposta passou a ser aceita, mas não se fazia ideia do porquê disso acontecer. Uns falaram em paradoxo, ainda outros em 2 respostas corretas, enfim. O programa que simulava o problema foi escrito de forma bem algorítmica, bem orientado a estatística mesmo e nada revelava sobre o insight da solução. Daí me veio a ideia de escrever um simulador com código orientado a objetos e bem fluente, de modo que o código se comunique conosco, dizendo o porquê de nossas chances aumentarem ao trocar de porta. Igual uma equação matemática ou um gráfico “falam” com a gente. Daí que surgiu a ideia do post. O ponto aqui não é recriminar as pessoas que não aceitaram a resposta do desafio, afinal de contas, eu também não aceitei de imediato.  Mas mostrar como a solução vem a tona facilmente se nos empenharmos em fazer um código que reflita o modelo de negócio de forma mais real possível. Isso se aplica ao Monty Hall Problem, ao jogo da velha que você fez no 1° período, ao sistema de GPS que você programou, ao sistema do seu banco e por aí vai.

Primeiramente, vamos entender porque a solução é realmente trocar de porta e depois vamos analisar como bom código, escrito com fluência, refletindo os requisitos do problema especificado, ajudam a entender a solução. Se você já leu até aqui, não pare agora 😉

 

Exagere sempre que puder


Sempre que você se deparar com um problema de lógica, uma boa abordagem é estressar o problema. Exagere. Tanto pra mais quanto pra menos, exagere! Claro, isso vai te ajudar a pensar não só no problema ali, mas em outras versões adaptadas do mesmo. Meio que estudando o problema olhando todos os seus lados assim como alguém estuda um cubo mágico antes de tentar completá-lo.

Vos  Savant fez isso ao dar a primeira dica da solução. Imagine que você tem, não 3 portas, mas 1.000.000 delas. Um carro e 999.999 bodes. Se o apresentador abrisse todas as portas exceto a 777.777, você trocaria bem rápido, não ? Esse primeiro argumento mata a questão. Porque se você diz que não trocaria, você está dizendo que a probabilidade de você estar certo na primeira vez que escolheu 1 entre 1 milhão de portas é a mesma que a probabilidade de você acertar agora com 1 entre 2 portas!  É danado né? Você ver o apresentador abrir 999.998 portas com bodes e ainda assim você achar que a sua porta tem um carro, e não a outra, a única que ele não abriu!

A segunda dica da solução é a mais simples, Marilyn Vos Savant desenhou todas as hipóteses possíveis e a conclusão vem rápido:

Possibilidades existentes

Se você não trocar de portas, você perde 2 em 3 vezes!

Analisando o problema com Orientação a Objetos


Como falei, o algoritmo feito inicialmente para o simulador era extremamente algorítimico: utilizando arrays, números pra representar portas, o main era o programa de auditório e por aí vai. É verdade que é bem mais rápido de se fazer se você quiser apenas provar que trocar é a melhor solução. Mas não dá pra ter insight nenhum da solução olhando pra um código não OO. Por isso, implementei um código bem simples em Java que possui uma representação mais realista. Temos assim, classe Porta, classe Participante e classe Show para retratar esse problema. Veja abaixo:

public class Show {

  private List<Porta> portas;

  private Participante participante;

  public Show(int quantidadePortas) {

    this.participante = new Participante();
 
    this.portas = new ArrayList<Porta>();

    for(int i = 0; i < quantidadePortas; i++) {
      this.portas.add(new Porta(i));
    }

    Porta premiada = sortearPorta();
    premiada.setPremiada(true);
  }

  public boolean simular(boolean trocar) {
    participante.escolherQualquerPorta(this.portas);
    abrirPortas();
    if(trocar) {
      participante.trocarPorta(this.portas);
    }
    return this.participante.getPorta().premiada();
  }

  public void setPortas(List<Porta> portas) {
    this.portas = portas;
  }

  private Porta sortearPorta() {

    int numPorta = sortearNumero(this.portas.size()-1);

    return this.portas.get(numPorta);
  }

  private void abrirPortas() {

    Porta escolhida = this.participante.getPorta();

    Porta portaRestante = null;

    this.portas.remove(escolhida);

    if(escolhida.premiada()) {
      portaRestante = sortearPorta();
    }
    else {
      for(Porta porta : this.portas) {
        if(porta.premiada()) {
          portaRestante = porta;
          break;
        }
      }
    }

    this.portas.clear();
    this.portas.add(escolhida);
    this.portas.add(portaRestante);
  }

  private int sortearNumero(int maximo) {
    return (int)Math.round(Math.random()*(maximo));
  }

  public List<Porta> getPortas() {
    return portas;
  }

  public static void main(String... args) {

    int acertos = 0;

    int rodadas = 100;

    int numPortas = 3;

    for(int i = 0; i < rodadas; i++) {

      Show show = new Show(numPortas);

      boolean resultado = show.simular(true);
      if(resultado) {
        acertos++;
      }
    }

    System.out.println("Taxa de acerto: " + 100*(((double)acertos/(double)rodadas)) + "%");
  }

}

class Participante {

  private Porta porta;

  public void escolherQualquerPorta(List<Porta> portas) {
    int numPorta = (int)Math.round(Math.random()*(portas.size()-1));
    this.porta = portas.get(numPorta);
  }

  //Troca pela primeira porta que não seja a dele
  public void trocarPorta(List<Porta> portas) {
    for(Porta temp : portas) {
      if(temp.getNumero() != this.porta.getNumero()) {
        this.porta = temp;
        break;
      }
    }
  }

  public Porta getPorta() {
    return porta;
  }

  public void setPorta(Porta porta) {
    this.porta = porta;
  }

}

class Porta {

  private int numero;

  private boolean premiada;

  public Porta(int numero) {
    this.numero = numero;
  }

  public int getNumero() {
    return numero;
  }

  public void setNumero(int numero) {
    this.numero = numero;
  }

  public boolean premiada() {
    return premiada;
  }

  public void setPremiada(boolean premiada) {
    this.premiada = premiada;
  }

}

Esse é o código pra simular o resultado do problema proposto.  Por default ele está trocando de porta. Se você quiser testar sem trocar de porta basta modificar o parâmetro do método ‘simular’ para false.  Mas o propósito disso tudo é analisar esse código. Um princípio muito importante de desenvolvimento de software é a chamada “Divisão de Responsabilidades”. Cada classe tem um propósito, e apenas um. O mesmo é válido para métodos.  As classes acima possuem essa característica.  Por exemplo,  a classe Participante reproduz as ações que o participante fará durante o jogo: escolherPortaAleatoriamente() e trocarPorta(). Participante não sorteia porta ou simula o jogo de alguma forma. Quem faz isso é a classe Show, que representa o jogo como um todo.  Dessa forma, o código fica mais fácil de entender. Logo, a chave para o problema ser do jeito que é também fica na cara. Por exemplo, no problema citado, uma parte do código que me dá um bom insight do porquê da solução é:

 


Porta escolhida = this.participante.getPorta();

Porta portaRestante = null;

if(escolhida.premiada()) {
  portaRestante = sortearPorta();
}
else {
  for(Porta porta : this.portas) {

    if(porta.premiada()) {
      portaRestante = porta;
      break;
    }
  }
}

Se você observar bem, verá que esse método revela a intenção do apresentador do programa, digamos, a mente dele ao manipular as portas. Note que existem 2 portas que são escolhidas pelo apresentador para NÃO serem abertas: ‘escolhida’ e ‘portaRestante’. Ou seja, a porta escolhida pelo participante inicialmente e mais uma porta restante. Sempre teremos 2 portas ao final: uma delas é a escolhida pelo participante e a outra… bem, aí que está a ‘mágica’. Observe como a outra porta é escolhida para permanecer fechada. O apresentador do programa não escolhe por acaso! Veja que o método ‘abrirPortas’ tem acesso a informações a coisas que supostamente não era pra ter. Mas são regras do jogo! Logo, ele sabe o que está por trás das portas. Vou repetir: A classe Show tem acesso a informações encapsuladas em todas as portas – se ela esconde um bode ou um carro. Participante só tem acesso a porta que ele escolheu. Agora observe a forma como o apresentador pensa pra decidir que portas ficarão fechadas. O if-else é como se estivesse perguntando a si mesmo: “A porta escolhida pelo participante  é a premiada? Se for, vamos escolher qualquer uma outra pra ficar fechada. Caso a porta que ele esteja agora tenha um bode, abra todas EXCETO a que tem o carro.” Então há ganho de informação na passagem da primeira escolha pra segunda! E o apresentador sabe de tudo isso pelo acesso que ele tem às portas. Na primeira escolha, o participante tem 1/3 de chance de acertar. Mas na segunda, o participante deve antecipar que o apresentador sempre vai abrir uma porta com um bode. O ganho de informação se concretiza quando o participante realiza que ele está de fato trocando a chance de apostar em uma porta pela chance de apostar em duas.  E no código isso fica explícito na forma do apresentador decidir que portas serão abertas.

Ganho de informação? É. Tipo, caso um E.T aterrise no programa logo após o momento que o apresentador abriu uma porta e saiu um bode, e ele veja que existem apenas 2 portas, onde em uma delas está o prêmio, com certeza as chances serão de 50%. Mas aí que tá: ele não teve o ganho de informação obtido no momento da abertura da porta!

Modifique o atributo ‘numPortas’ no método main para 10.000 e veja como as suas chances aumentam. Isso corresponde ao primeiro argumento de Vos Savant para trocar de portas. Repare que a cada porta existente nesse caso, o apresentador irá pensar de forma a só abrir as portas com bodes. Ganho de informação multiplicado… Se eu fosse você, trocaria fácil!

Agora é com você… pronto pra colocar o Sérgio Mallandro no bolso?

Padrão
Experiências, Lógica

Números “aleatórios” – Random

Você já se perguntou como o Math.random() gera um número aleatório ? Já? Bom, eu me perguntava bastante. Tanto que um dia resolvi revirar o que havia por trás dele. Isso já faz algum tempo, mas de qualquer forma, é algo interessante pra postar aqui.

Antes de mais nada não podemos descrever esses números como aleatórios ou randômicos porque é 100% previsível o número que o Math.random() vai gerar. Tá, tudo bem. Pra quem não se apega a detalhes e acha que isso é apenas semântica, vai em frente e chama isso de aleatório. Mas eles não são! São pseudoaleatórios. “Pseudo” porque são previsíveis como já falei. Como assim previsíveis? Veja. Em computação, existem os chamados “Pseudo Random number generators” ou geradores de números pseudo-aleatórios. Esses são baseados em uma equação que, recebendo como parâmetro um valor numérico, retorna uma série numérica. Os números que compõem essa série são normalmente bem diferentes uns dos outros (primos, ímpares, primos entre si, múltiplos de 7, próprio zero e assim por diante), o que podemos dizer que a equação gera números randômicos. O problema é que com o tempo, essa série começa a se repetir.

Mas… peraí! É previsível o número que o Math.random() vai gerar? Hã… sim! Ora, é baseado numa equação. E java.util.Random utiliza LCG  (Linear congruential generator):

 

Equação LCG

Equação LCG

Obviamente existem outras equações pra gerar essas sequências. Inclusive Von Neumann mesmo inventou uma (não muito boa por sinal).

Vamos ver na prática agora, como o Random gera a mesma sequência “aleatória” numa aplicação bem simples:

<code>

</p>

<pre>import java.util.Random;

public class GeradorAleatorio {

    private Random random;

    public GeradorAleatorio(long semente) {
         random = new Random(semente);
     }

     public int gerarInt() {
          return random.nextInt();
     }

     public long gerarLong() {
          return random.nextLong();
     }

     public double gerarDouble() {
          return random.nextDouble();
     }

     public static void main(String[] args) {
           GeradorAleatorio gerador =
           new GeradorAleatorio(10);
           long l = gerador.gerarLong();
           System.out.println(l);
      }
}

</code>

Pode rodar o código quantas vezes quiser. A sequência gerada sempre será “-4972683369271453960” seguido de “4755622236989466036“. Como o Random trata pra te entregar um valor double mais “bonitinho” é outra história.

Enfim, a questão é que a gente acabou de ver que esses números são sempre os mesmos pra o mesmo valor inicial passado (ou seed – semente). Daí surge a pergunta: “Como gerar números imprevisíveis?” Bom, existe uma técnica bem comum que é passar o System.currentTimeMillis() como seed ou valor inicial. Como esse valor corresponde ao número de milissegundos passados desde 1970 pra o tempo atual, esse valor irá variar numa amplitude maior ainda. Tornando-o estatisticamente imprevisível.

<code>
    GeradorAleatorio gerador =
    new GeradorAleatorio(System.currentTimeMillis());
    long l = gerador.gerarLong(); 
    System.out.println("O long aleatório foi: " + l);

</code>
Agora pode rodar o código quantas vezes quiser que o resultado será sempre diferente. Note que em teoria, o número sorteado ainda é previsível. Imagine que o mundo congelou no momento que a semente é passada para o construtor da classe Random e você pôde ver que número foi esse. Jogando ele na equação, você poderia prever com 100% de certeza que número seria sorteado. Mas enquanto o mundo não congelar tendenciosamente para alguns, o truque está seguro.

public class GeradorAleatorio {

 

private Random random;

 

public GeradorAleatorio(long semente) {

random = new Random(semente);

}

 

public int gerarInt() {

return random.nextInt();

}

 

public long gerarLong() {

return random.nextLong();

}

 

public double gerarDouble() {

return random.nextDouble();

}

 

public static void main(String[] args) {

GeradorAleatorio gerador = new GeradorAleatorio(System.currentTimeMillis());

long l = gerador.gerarLong();

System.out.println(l);

}

 

}

Padrão
Experiências

Thoughtworks Brasil e outras histórias

Com a chegada da Thoughtworks no Brasil e o crescimento rápido da empresa, reparei que muita gente se pergunta como funciona o processo seletivo deles, se é igual ao processo nos E.U.A e na Europa, se é diferente, enfim, as dúvidas são muitas. Consultando o google, consegui encontrar alguns blogs que contam as experiências de alguns profissionais que passaram pelo processo seletivo da TW. Essas experiências ajudam bastante a outros que estão percorrendo esse caminho. Por outro lado, não encontrei nenhum blog em português falando sobre o assunto. Ou ainda, nenhum brasileiro contando a experiência de participar do processo seletivo da Thoughtworks aqui no Brasil. Pois bem, eis me aqui. O assunto desse post é basicamente as lembranças que tenho de ter participado no processo da TW e o que senti\guardei sobre a empresa.

Thoughtworks

Thoughtworks

Primeira Entrevista – Telefone


3 dias após enviar meu Curriculum em inglês para o email citado no site, recebi um email da recruiter me agradecendo o interesse na vaga e pedindo para eu me informar sobre a história, cultura e organização da empresa, pois dentro de alguns dias ela iria me contatar por telefone. Poucos dias depois, recebi uma ligação da recruiter da TW. Nesse primeiro contato, a recruiter estava interessada em saber como eu obtive conhecimento da vaga, o que eu sabia sobre a empresa, os valores da empresa que me chamavam atenção e etc. No email tinha sido pedido para estudar um pouco sobre a empresa, seus valores, objetivos e coisas do tipo, e eu aconselho que você realmente o faça para entender melhor como eles pensam software. Caso contrário, você pode parecer meio bobo nessa primeira entrevista. Ela também confirmou alguns dados que eu passei no CV (ferramentas, linguagens que conhecia, inglês fluente – que é essencial) e perguntou mais a fundo sobre a minha área de atuação. A última pergunta (e a mais interessante) foi: “Por que você quer trabalhar na Thoughtworks ?” Essa é uma pergunta tão previsível que chega a ser clichê em entrevistas de emprego. Mas essa foi interessante, porque nesse caso, trabalhar na TW é completamente diferente de trabalhar em qualquer empresa. E justamente por isso, sua resposta deve se basear nos valores da empresa. Por isso que eles ressaltam a importância de pesquisar mais sobre os valores e princípios da TW. Para checar se seus valores estão mais ou menos alinhados com a da empresa.

A recruiter foi bem gentil (como todo mundo que eu conheci lá) e claramente tinha a intenção de deixar a entrevista bem aberta. Muito mais uma conversa informal sobre meus interesses e habilidades do que uma entrevista formal de emprego. A impressão deixada após essa primeira entrevista é de que eles não estão brincando quando dizem que procuram “apenas os melhores e mais apaixonados por programação”. E caso você transpareça insegurança, falta de sinceridade ou falta de paixão/motivação, é pouco provável que eles entrem em contato novamente.

Desafios práticos


Assim que a entrevista terminou, a recruiter enviou por email 2 desafios de programação muito interessantes:

O primeiro deles era o problema conhecido como “Mars Rovers” e não é nem um pouco difícil de se entender. A questão aí não é se você é capaz de resolver. Mas como você vai resolver. O problema é bem simples e você não deve implementar uma solução de outro mundo, utilizando vários Design Patterns, sob a pena de perder pontos diante do avaliador. De qualquer forma, se sua solução for simples o suficiente, futuramente se eles pedirem pra você modificar ou adicionar features ao projeto, você vai conseguir fazer isso com um pé nas costas. Se você resolve querer impressionar logo de cara e implementar uma solução complexa, você pode se complicar na hora de modificar o projeto. É realmente uma questão de K.I.S.S – Keep it simple, stupid. Não vou divulgar o problema aqui (no google você encontra fácil), nem tampouco a minha solução. Acredito que se você sabe programar e é bom nisso, não precisa conhecer a solução de outros para fazer a sua própria. Além do mais, isso fugiria completamente do propósito do post.

O segundo problema é “Trains” e é bem mais complexo do que o primeiro. Enquanto o primeiro problema, por ser mais simples, dá mais liberdade para você utilizar várias abordagens de design diferentes, o segundo gira em torno de encontrar rotas existentes entre cidades num dado mapa. Basicamente, consiste de navegar através dos vértices de um grafo, encontrar o caminho mais curto entre dois vértices  e encontrar caminho mais curto passando obrigatoriamente por um subgrafo. Muito divertido de se fazer, porém mais difícil. É necessário um bom conhecimento de recursão e do algoritmo de menor caminho de Dijsktra para resolver todas as proposições do problema.

Escolhi o “Mars Rovers” por ser mais simples e, logo, mais fácil de modelar (coisa que eu adoro fazer). É possível você usar uma abordagem, depois desmanchar e usar outra, mesclar mais de uma abordagem, voltar atrás, mudar tudo… o escambal! É muito importante você ter um motivo sólido para escolher seu problema. E depois que escolher, dedique-se a solução! Pessoas que gostam de dizer coisas do tipo “eu fiz de última hora”,  “não tive muito tempo pra fazer” ou “se eu tivesse mais tempo, faria melhor” não são levadas a sério. Obviamente são meras desculpas (e são fáceis de detectar) usadas como válvula de escape caso o avaliador ache algum problema ou erro na solução. Pessoas assim revelam que são de fato inseguras e que não são motivadas o suficiente. Essa etapa é uma das mais importantes de todo o processo e você deve levá-la a sério pois eles levam a sério.

Após o envio do email com o enunciado dos problemas (em inglês), tive 3 dias para resolvê-los e enviá-los para a TW. Se você acha que não conseguirá enviar a solução em 3 dias, você pode enviar um email para a pessoa em contato (no meu caso, a própria recruiter) pedindo um tempinho extra. Eles são flexíveis quanto a isso, desde que você avise de antemão. São flexíveis também em relação a qual linguagem você prefere. Se você prefere Java, faça em Java, se prefere Ruby, faça em Ruby, se prefere Haskell, faça em Haskell. Mas lembre-se: não utilize uma linguagem que você não conhece bem só para impressionar os avaliadores. Os caras lá são muito bons e vão perceber se você não conhece bem a linguagem e quer apenas causar impressão. Mais uma vez você vai ficar com cara de bobo.

Assessments – and bring your brains


Algumas semanas depois de enviar meu problema, a Thoughtworks promoveu um evento chamado “Café Ágil” em Recife. Neste evento, palestraram Paulo Caroli, coach da equipe deles em Porto Alegre, e Jim Webber, consultor da TW em Londres (se não me engano). O objetivo era também chamar a atenção de mais candidatos as vagas abertas em POA. Enfim, nesse dia, à tarde, a TW organizou uma bateria de testes com os candidatos as vagas. Essa bateria é o que eles chamam de assessments e no meu caso foi realizada num hotel em Recife. Onde eles explicaram um pouco sobre a história da empresa, apresentaram a visão, o ideal e o ambiente da TW. De fato, todos que trabalham lá estão muito orgulhosos por estarem onde estão, num ambiente onde a criatividade impera. As pessoas que eles contratam são realmente muito boas no que fazem e, de quebra, muito gente fina. Sim, eles procuram esse traço nos candidatos. Nas palavras de um dos avaliadores: “We don´t hire jerks”.

Tá bom, agora sobre o assessment. Eu cheguei no hotel às 14h e logo começou a apresentação que eu mencionei acima. Às 14:30h estava começando a série de provas:

– A primeira prova é o conhecido Wonderlic. Esse teste é usado pela NFL (Liga profissional de futebol americano) para testar se os jogadores possuem uma pontuação razoável para jogar futebol profissionalmente (futebol americano é um jogo complexo e é preciso ser inteligente pra jogar – na maioria das posições). O teste consiste de 50 questões, que vão crescendo em complexidade de acordo com o número da questão (a questão 50 é a mais difícil), e é dado um total de 12 minutos para responder o máximo que você conseguir. As questões variam de assunto, podendo ser matemática básica, interpretação de texto, geometria básica, lógica e língua inglesa. Detalhe: o Wonderlic é em inglês assim como todos os testes no assessment são em inglês.  Você pode encontrar simulados do wonderlic aqui.  Segundo o wikipedia, existe uma média de acertos para cada profissão. Químicos tem uma média de 31 acertos. Programadores 29. Acho que é bom ficar dessa média pra cima.

– A segunda prova tem 7 questões de puro raciocínio lógico. Chega a lembrar uma máquina de Turing. Mas não é estudando Máquina de Turing e seus conceitos que você vai ser mestre nessa prova. Não, não. É lógica mesmo. Trata-se de um sistema de fluxo de controle, onde são dadas instruções e um fluxo para você sair seguindo de acordo com as instruções dadas e os resultados encontrados no fluxo. No fluxo existem estruturas de repetição, condicionais, “goto” e etc.  No fim do problema é encontrado um resultado, que você deve assinalar na prova. São 7 questões e você tem 1 hora para fazer o máximo que conseguir. A chave para essa etapa é CONCENTRAÇÃO. Depois vem velocidade. Digo isso porque tentei ser cauteloso na minha prova, examinando e reexaminando após cada questão para verificar o resultado. No fim, só fiz 5 questões. Acho que acertei todas porque passei dessa etapa. Mas mesmo assim, seria melhor me concentrar 100% e fazer as questões rapidamente sem checagem e talvez desse um tempo de checá-las no final. Mais uma vez – a chave é velocidade.

– A terceira prova é o Wonderlic denovo. Mas dessa vez não houve limite de tempo. Você tenta acertar o máximo possível, olhando as questões com calma e tudo mais. Inclusive você pode corrigir alguma questão que você porventura ache que errou no primeiro teste. Mas a pontuação feita na primeira prova já foi computada e suas correções só valem para essa outra avaliação.

As provas são extremamente cansativas (principalmente se você tá dando seu máximo), esgotantes, mas também muito divertidas pra quem gosta de desafios. Após as provas fui chamado para uma conversa em inglês com os palestrantes do Café Ágil. Nessa conversa, eles fizeram perguntas sobre… resumindo – “o que significa ser ágil de fato?” e pediram meu ponto de vista. Acredito que nessa fase eles analisam seu inglês, sua personalidade (tipo, se você tá só puxando saco e coisa e tal, obviamente isso vai ser notado), sua visão de equipe, ética e, por último , seu conhecimento em metodologias ágeis. A conversa se delongou por mais ou menos 1 hora de tão legal que tava. Nisso, já eram 21:30h e todo mundo só queria ir pra casa.


Entrevista técnica por telefone


O problema que eu enviei do “Mars Rovers” foi examinado por um desenvolvedor da TW por algumas semanas e a recruiter entrou em contato novamente para marcarmos uma data para fazer uma entrevista técnica por telefone. Na data marcada, eu tinha acabado de chegar do trabalho e me ligaram. De imediato deu pra notar que não era uma entrevista comum. O cara, que por sinal era muito bom e tinha sido a primeira contratação deles no Brasil, usou um tom bem descontraído e jamais nem sequer tentou dominar a conversa, como é comum acontecer nesses casos. Pelo contrário, eu falei bastante sobre o que eu achava e pensava sobre um monte de tendências da área de TI. Porém, algumas perguntas bem técnicas foram feitas, tipo:

– Sobre o que era o projeto no qual eu estava trabalhando? E o anterior ? O que você sugere/sugeriu como melhora para ele?

– Já foi commiter de algum projeto open source?

– Quais frameworks/APIs/ferramentas você usa ou já usou? Pediu para eu explicar algumas delas.

– Qual sua linguagem favorita? Se você fosse mudar algo nela, o que seria?

– Você trabalha num ambiente ágil? Qual sua metodologia/técnica ágil favorita?

As perguntas não foram feitas em sequência e o entrevistador não esperava respostas 100% consistentes com definições. Na verdade, em algumas perguntas, após eu responder, ele também dava a opinião dele. Muito divertido, foi tipo uma conversa de amantes do desenvolvimento de Software. Algumas perguntas pessoais foram feitas também, tipo, livros que eu mais gosto (tanto da área quanto fora), o que eu achava de mudar pra Porto Alegre e etc. Essa conversa demorou cerca de 20 minutos e daí passamos a falar da minha solução do problema enviado. Decisões de design que eu tomei, por que eu as tomei e se eu faria diferente depois. Essa parte não demorou muito, foi mais pra sentir o meu conhecimento sobre o código escrito e pra sentir a minha mentalidade frente a um problema de programação simples. Não tenho certeza disso, mas acho que caras que respondem coisas do tipo: “eu não mudaria nada”, “essa solução é completa” ou  “meu jeito é melhor”  devem perder pontos na sua avaliação. No total a conversa demorou uns 30 minutos, mas passou voando!

Super Saturday em Porto Alegre


Após a fase de entrevista técnica, recebi um email me convidando para comparecer ao “Super Saturday” em Porto Alegre. O Super Saturday é um evento promovido pela Thoughtworks para os candidatos conhecerem melhor as pessoas da empresa e vice-versa. Nesse evento, o candidato é submetido a mais algumas análises, dessa vez por vários membros da TW. É uma grande oportunidade, talvez a maior, de mostrar seu potencial e conhecer o deles. Então a TW paga sua passagem e sua hospedagem em Porto Alegre. Basicamente, foram realizados 3 testes:  análise de código, entrevista sobre cultura e ética e pair-programming, não necessariamente nessa ordem.

– Análise de código: Essa parte é bem interessante. Me foi entregue uma página com um enunciado de uma questão de programação a ser implementada e uma implementação. Como se alguém tivesse implementado a questão proposta. O problema é que o código escrito é propositalmente cheio de erros e gambiarras. Erros sintáticos, erros semânticos, más práticas de programação, modelagem tosca, enfim… tudo aquilo que bons programadores têm pesadelos à noite. Não é difícil encontrar os erros . Eles tão bem na cara, mas visto que são muitos, você pode deixar algum passar. Fique atento a isso. Desmascare todos os erros possíveis e imagináveis.

– Entrevista ética e cultural: Muito boa também. Alguns membros da equipe irão conversar descontraidamente com você sobre situações que podem ocorrer no dia-a-dia de um Thoughtworker. De vez em quando vão bolar algumas situações extremamente constrangedoras e perguntar “o que você faria?”. As situações são hipotéticas e algumas perguntas são capciosas, mas se você não é um “jerk”, deve ficar tranquilo e responder do fundo do seu coração, sem esconder o que realmente pensa. Afinal, esse é o objetivo dessa fase. Saber um pouco sobre que valores você preza e se seus valores e pensamentos estão de acordo com os da empresa. Simplificando: Be nice.

– Pair-programming: Lembra do desafio de programação que você deve implementar? Pois é, ele volta. E com mais funcionalidades. Você deve estar preparado para colocar mais funcionalidades no seu projeto e fazer isso da forma mais simples possível. Nessa fase eles verificam se você sabe programar em par (coisas que eles fazem quase 100% das vezes lá) e se você se comporta bem trabalhando com alguém do seu lado. É observado se você é uma pessoa que aceita sugestões, é amigável, sabe trocar idéias e é eficaz ao bolar um plano rápido de implementação. Recomendável ter prática com TDD também.

Voltando de Porto Alegre, aguardei algumas semanas até a TW entrar em contato novamente comigo. O que aconteceu foi que no Super Saturday eu não estava muito bem. Acordei no sábado com uma febre moderada, mas consegui segurar a onda. Logo após o almoço foi a minha entrevista de Pair programming. Falei que não estava me sentindo muito bem mas dava pra continuar com a entrevista. Resultado: fui muito mal. Não consegui me concentrar muito bem e, no final de uma hora, tinha desenvolvido pouquissima coisa. Achei que estava eliminado, mas acho que eles levaram em conta que eu consegui bolar a solução pro problema, apesar de não ter conseguido implementar a solução no tempo. Me deram mais uma chance e me colocaram pra fazer pair programming online com o consultor líder deles, um desenvolvedor superstar aqui no Brasil. Enfim, não fui tão mal. Dessa vez, consegui junto com ele colocar boa parte da solução em prática.

Outras histórias


Bem, fui aprovado na seleção da TW, mas infelizmente, ao analisar a proposta feita, cheguei a conclusão que o custo-benefício não compensava pra mim. É muito bom saber que uma das maiores empresas de Software do mundo, que só contrata os melhores e mais apaixonados pela profissão, me notaram positivamente. Mas no fim do dia, o que conta é o que você mesmo acha que vale a pena para a sua vida. Na minha forma de ver as coisas, uma mudança grande dessas, exigiria um benefício maior por parte da TW. Não se pode apenas confiar que, porque a empresa é “super cool” e tem muitos desenvolvedores top notch, um pensamento diferenciado da maioria no ramo de TI, qualquer coisa vai valer a pena para entrar lá. Obviamente, eu morando em POA seria completamente diferente mas enfim… Essa é apenas a minha opinião. Nem todos pensam assim. Tenho certeza que muitos aprovados decidiram se mudar pra lá e não se arrependeram disso. Mas cada caso é um caso e no meu caso, não senti a confiança necessária pra aceitar a proposta.

De qualquer forma, adorei a forma como eles tratam os candidatos – como potenciais colegas de trabalho. Todos com quem conversei são educados e super gente fina. Sem dúvida as pessoas são o maior bem que eles possuem e isso fica evidente desde o primeiro contato. Creio que essa mentalidade deve ser exercitada por mais empresas no Brasil. Da mesma forma, como profissionais devemos sempre nos aperfeiçoar esperando esse tipo de oportunidade. Ou criá-las.

Padrão
Desenvolvimento Web

Filtro Anti-XSS em Java

Há um bom tempo, na empresa onde trabalho, enfrentei uma situação curiosa. Eu ainda estagiava e um colega (hoje na globo.com) me perguntou: “quão segura é a aplicação que você está desenvolvendo?”. Eu, muito ingênuo (bem, muito mais do que hoje), respondi que era possível encontrar bugs, mas nada catastrófico a ponto de pôr tudo a perder. Outro colega (talvez mais ingênuo) teve a audácia de dizer que o sistema sob sua responsabilidade era livre de erros de segurança de qualquer tipo. A questão voltou-se completamente contra o programador super-confiante. Todos começaram a pensar em formas de encontrar bugs catastróficos e logo, o primeiro colega, autor da questão inicial, encontrou a brecha. Em poucos minutos, ele mostrou como cadastrar um item num dos cadastros do sistema para que, quando alguém clicasse nesse item, o sistema direcionaria o usuário para o site do google (ele foi bonzinho, poderia direcionar para algo bem pior…). Não preciso mencionar que o responsável pelo sistema ficou sem fala… Logo após, ele mencionou como era fácil encontrar sites expostos a este tipo de ataque e mostrou uma meia dúzia deles. Nos próximos dias comecei a trocar umas idéias com alguns desenvolvedores sobre como defender-se de ataques XSS. Esse post mostra basicamente uma forma de defesa “server-side” contra esse tipo de ataque (mais especificamente em sistemas Java, mas obviamente, a idéia se aplica a muitas outras linguagens).

Primeiro, uma tentativa de definição do que é Cross-Site Scripting:

Cross-Site Scripting (XSS) é um tipo de vulnerabilidade que pode existir em aplicações Web, na qual o ataque começa no lado do cliente, “atravessa” o sistema e permite ao atacante obter informações não permitidas de outros usuários que acessam ou acessaram a aplicação. Isso é feito por meio de injeção de código malicioso dentro das páginas web acessadas pelos usuários.

Ataque XSS

Ataque XSS

Esses ataques normalmente são códigos javascript inseridos por meio de campos de texto da aplicação e é bem fácil de simular. Aqui você encontra como simular um ataque XSS. É bem simples mesmo. Da mesma forma, a solução que eu utilizei é bem simples, é server-side e aqui vai um passo-a-passo de como implementar isso:

1 – A idéia é construir uma classe para tratar a requisição (request) da forma como você deseja. No meu caso, precisávamos tratar requests com tags html, como por exemplo “<script>” e “<a href>”. Um método estático que recebe a String original e retorna uma String modificada (do jeito que você precisa fazer) deve dar conta do serviço. Para casos mais gerais – e acredito que tratar TODAS as tags do tipo “<*>” – aconselho usar expressões regulares.

– Exemplo de classe TratarTags:


public class TratarTags {

    /*Expressão representando todas as expressões com tags html. "?"
    significa não-gulosa(non-greedy).*/
    private static final String expressaoComTags = "<.*?>";

    public static String retirarTags(String html) {

        String retorno = html.replaceAll(expressaoComTags, "");
        return retorno;
    }
}

2 – Construir o filtro propriamente dito. É necessário que você implemente a interface javax.servlet.Filter que consiste dos métodos init, destroy e – chegaremos lá – doFilter. Dentro do init e do destroy não tem muito o que fazer. O Container Web (uso o Tomcat 6.0.14 no momento) é esperto o suficiente pra saber o que fazer com esses caras. Particularmente falando, coloquei um log pra sinalizar inicialização e destruição do filtro.

Dentro do doFilter() – e é agora que começa a ação – você deve pegar o objeto do tipo HttpServletRequest, decorá-lo dentro de um tipo que você criará (ver passo 3) e passá-lo adiante para o container. Daí em diante, o container decidirá se enviará o request embrulhado, ou seja, seu objeto “request” criado no passo 3, para o próximo filtro (se existir um) ou para o servlet em questão. Para passá-lo adiante é bem simples. Pegue o objeto FilterChain que foi passado por parâmetro e chame o método doFilter do mesmo passando o request embrulhado e o response (você também pode alterá-lo se quiser! =D).

3 – Para entender esse passo você precisa ter ao menos uma noção do que é um Decorator. Tá. É um padrão de projeto, eu sei. Quase todo mundo sabe. O que eu quero dizer é que você precisa ENTENDÊ-LO.

A má notícia é que você não pode alterar o request em si e passá-lo adiante. Por outro lado, ninguém nunca falou nada sobre passar um request “de mentirinha” para o container. É isso que você faz basicamente. Você cria um objeto wrapper, no caso um RequestWrapper, que faça tudo que um HTTPServletRequest normal faz. “Tenho que implementar a interface de HttpServletRequest então…” você pensa. Você até pode. Mas essa interface possui uma monstruosidade de métodos que, acredite em mim, você não vai querer implementar um a um. Por isso já existe na API Java Servlet uma classe chamada HttpServletRequestWrapper, a qual você só modifica os métodos que você quiser! É só estendê-la! Agora ficou fácil. Bom, pra tratar tags html eu sobrecarreguei todos os métodos getParameter* para pegar o valor do getParameter* original, aplicar a regra da minha classe TratarTags (passo 1) e retornar o valor modificado. Sempre que uma requisição chegar ao servlet e você quiser extrair um valor do request(um parâmetro por exemplo), esse valor já chegará SEM as tags. O que significa que o filtro deu conta do trabalho.

Exemplo: TratarTagsFilter:

public class TratarTagsFilter implements Filter {
    private MapeamentoUrlCampo mapeamento = null;

    public TratarTagsFilter() {
        this.mapeamento = MapeamentoUrlCampo.getMapeamento();
        this.mapeamento.addMapeamento("campanhaAction.do", "script");
     }

     private static final Log logging =
     LogFactory.getLog(TratarTagsFilter.class);

     public void destroy() {
         logging.info("Terminando de executar o filtro. Classe= " + this);
     }

     public void doFilter(ServletRequest request,
     ServletResponse response, FilterChain chain) throws IOException,
     ServletException {
         logging.info("Executando o filtro neste momento. Classe= " + this);
         RequestWrapper requestWrapper = new RequestWrapper(request);
         chain.doFilter(requestWrapper, response);
     }

         public void init(FilterConfig arg0) throws ServletException {
         logging.info("Iniciando o filtro neste momento. Classe= " + this);
     }
     /*** Classe Interna estática que adiciona uma nova
     funcionalidade aos métodos da Classe HttpServletRequest* */
     private static class RequestWrapper extends HttpServletRequestWrapper {

         public RequestWrapper(ServletRequest request) {
             super((HttpServletRequest)request);
         }

         @Override
         public String getParameter(String name) {
             String valor = super.getParameter(name);
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valor != null && !mapeamento.permiteCampoHtml(urlAtual, name)){
                 String novoNome = TratarTags.retirarTags(valor);
                 return novoNome;
             }
             else {
                 return valor;
             }
         }
         @Override
         public Map getParameterMap() {
             Map<String, String> map = super.getParameterMap();
             Set<String> chaves = map.keySet();
             List<String> listaChaves = new ArrayList<String>(chaves);
             Map<String, String> mapRetorno = new TreeMap<String, String>();
             for(String chave : listaChaves) {
                 mapRetorno.put(chave, this.getParameter(chave));
             }
             return mapRetorno;
         }
         @Override
         public String[] getParameterValues(String name) {
             String[] valores = super.getParameterValues(name);
             String[] novosValores;
             String urlAtual = this.getRequestURL().toString();
             MapeamentoUrlCampo mapeamento = MapeamentoUrlCampo.getMapeamento();
             if(valores != null && !mapeamento.permiteCampoHtml(urlAtual, name)) {
                 novosValores = new String[valores.length];
                 for(int i = 0; i < valores.length; i++) {
                     novosValores[i] = TratarTags.retirarTags(valores[i]);
                 }
                 return novosValores;
             }
             else {
                 return valores;
             }

         }
    }

}

4 – Agora basta configurar o filtro construído no seu web.xml. Dentro do elemento web-app você cria
duas tags:

A primeira é:


<filter>
    <filter-name>TratarTagsFilter</filter-name>
    <filter-class>com.projeto.filtro.TratarTagsFilter</filter-class>
</filter>

Que você direciona para interceptar apenas um padrão específico de url dessa forma:


<filter-mapping>
    <filter-name>TratarTagsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Pronto, o filtro está pronto pra funcionar na sua aplicação. Não sei se existe outra estratégia de evitar ataques via XSS fora essa de retirar tags html arriscadas. Sei que essa funciona eficientemente, por isso decidi utilizá-la. No entanto, se algum leitor souber de outra abordagem interessante, ficaria muito curioso em ouvi-la.

Padrão
Spring

Spring – Migrando de XML para Annotations

Introdução

Com certeza, você já abriu um arquivo do tipo applicationContext.xml e se deparou com um arquivo xml gigantesco com um monte de declarações de beans, onde se define em cada um deles, o nome de identificação, o nome completo da classe concreta, o escopo, o tipo da injeção de dependência e etc. do tipo:

<bean name="/meuBean"
scope="prototype" autowire="autodetect"/>

Más notícias pra quem encontrar esse tipo de abordagem no seu projeto. Primeiro, as configurações internas do seu projeto estão tornando a implementação de qualquer nova funcionalidade dependentes de um arquivo xml externo as classes. E isso significa alto acoplamento, ou seja, má idéia. Segundo, é muito mais difícil pra quem está aprendendo um novo sistema (uma manutenção por exemplo) se deparar com arquivos contendo um monte de beans a serem injetados pelo Spring e entender o propósito deles. Além desses 2 motivos, um ou mais xml´s enormes dão um aspecto lixoso ao projeto, quando uma solução mais elegante poderia ser empregada.

A boa notícia é que o Spring Framework já provê um melhor suporte a forma como seus beans são injetados pelo container de Injeção de Dependência – Annotations! Nesse artigo, você verá como é simples migrar da forma antiga de injetar dependências (XML) para a nova forma (Annotations) usando o melhor das novidades do Spring 2.5.

@Autowired – injetando dependências

Bom, vou levar em consideração que o leitor já conhece o funcionamento do container de IoC do Spring. Tendo dito isto, vamos direto ao ponto da mudança principal: a migração de xml para annotations. A annotation que o Spring disponibiliza para marcar o objeto injetado é @Autowired. Você pode encontrar mais informações técnicas sobre ele aqui. Você pode marcar um atributo, um setter ou um construtor como @Autowired, de acordo com sua preferência/necessidade. Pessoalmente, eu prefiro marcar os atributos de classe mesmo. Por que? 2 motivos:

1 – É mais simples. Simplesmente. Não preciso escrever um setObjeto nem um construtor que, no fim, só será usado pelo framework. Se eu não uso, não deveria escrever. Ou seja, menos verbose.

2 – Programando voltado-a-interfaces, se eu anoto meu atributo, uma interface, como @Autowired, o container IoC irá buscar apenas a única implementação daquela interface. Caso você tenha mais de uma implementação e quer escolher uma, pode usar o @Qualifier para escolher o bean mais especificamente. Nesse caso, aumentaria a verbose.

Para exemplificar, na sua classe que usaria uma dependência, você faria assim:

public class LoginAction {
    private final Log logger = LogFactory.getLog(LoginAction.class);

    @Autowired
    private IControladorHorario controladorHorario;
}

@Component, @Service, @Repository – dando nome aos bois.

Na configuração dos beans por xml era necessário dizer o id dos beans, ou um nome de identificação. Com annotations não é diferente. Só que é BEM mais fácil dar nome aos bois. Você simplesmente usa a annotation @Component na classe do bean que será injetado e coloca o id do bean entre os parênteses. Exemplo:

@Component("controladorHorario")
class ControladorHorario {...}

Na verdade, você deve optar por usar annotations mais específicas como @Service para controladores e @Repository para Daos/Repositórios. São os chamados stereotypes. Essas Annotations têm o mesmo efeito de @Component e estendem esta.

Parece mágica, mas não é. Ou é?

Nada demais até aqui. Repare que estamos injetando um controlador numa Action (Struts). É, eu sei. Parece mesmo mágica, mas o que ocorre na verdade é que, para o container saber onde detectar annotations você precisa declarar no seu applicationContext.xml o conjunto de pacotes onde ele deve procurar. Já vi casos onde são usados 3 contextos na aplicação (um para beans das actions, um para beans dos controladores[controladores do model] e um para beans do Dao). Nesse caso devemos indicar que beans procurar em cada um desses contextos (eu sei, é mais complicado e você normalmente deve usar apenas um applicationContext mesmo). No caso, no meu contexto de actions, eu indico onde estão os beans dos controladores, porque as actions injetam controladores. No meu contexto de controladores, eu indico onde estão os beans dos daos, porque os controladores injetam os Daos. Por exemplo, no contexto onde declararia os beans dos actions:

<beans xmlns="<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>"
xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>"
xmlns:context="<a href="http://www.springframework.org/schema/context" target="_blank">http://www.springframework.org/schema/context</a>"
xsi:schemaLocation="<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>
<a href="http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans-2.5.xsd</a>
<a href="http://www.springframework.org/schema/context" target="_blank">http://www.springframework.org/schema/context</a>
<a href="http://www.springframework.org/schema/context/spring-context-2.5.xsd" target="_blank">http://www.springframework.org/schema/context/spring-context-2.5.xsd</a>">

    <context:annotation-config/>
    <context:component-scan base-package="com.provider.discador.horario.controle">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
</beans>

– A tag “<context:annotation-config/>” indica para o framework que você está declarando seus beans via annotations.
– A tag “<context:component-scan base-package=”com.provider.discador.horario.controle”>” indica aonde o container deve fazer uma busca por annotations relacionadas com injeção de dependência. Encontrando essas annotations, o container produz as instâncias necessárias e injeta aonde o usuário indicar por meio do @Autowired. A tag interna a esta é “<context:include-filter type=”annotation” expression=”org.springframework.stereotype.Service”/>” e é utilizada como filtro para o container. Como se dissesse: “Ei! Vá nesse pacote e procure apenas por beans anotados por @Service”. Você pode optar também por usar um filtro de exclusão ao invés de inclusão como o exemplificado agora. Aprenda mais sobre essa mágica aqui.

“Mas não é melhor mandar o container fazer scan de todos os pacotes procurando todos os tipos de annotations logo? Ao invés de perder tempo especificando os pacotes que serão incluídos/excluídos?” A resposta é: NÃO. Tecnicamente você pode. Mas o overhead é significativo. Pense nisso, o container do Spring vai ter que fazer uma busca em toda a aplicação, preparando beans, dentro de beans, dentro de beans… enfim, tudo isso é custoso. “Mas minha aplicação é pequena, não tem problema…” talvez diga. Acontece que de qualquer forma, você está sobrecarregando uma capacidade do framework sem necessidade. Se você não precisa carregar todos os beans de todos os pacotes, e mesmo assim o faz, isso revela que você não tem muitos critérios como desenvolvedor. Tipo matar mosquito com bazuca.

Uma abordagem a evitar

Apesar de todas as facilidades que essa técnica possibilita, existe um problema de conflito com um design facilmente encontrado por aí – HibernateDaoSupport. Essa classe faz parte do framework Spring e é bastante simples ao disponibilizar serviços de um DAO via Hibernate. O HibernateDaoSupport, com o objetivo de salvar objetos na sessão e consequentemente no banco de dados, possui uma SessionFactory que é inicializada uma vez assim que a aplicação começa a rodar. Você talvez configure essa SessionFactory no seu contexto de dados do spring. Então, se você deseja injetar uma classe Dao que estenda direta ou indiretamente HibernateDaoSupport você terá problemas. O problema começa uma vez que você chama getHibernateTemplate(). Visto que você estende o HibernateDaoSupport, a chamada a getHibernateTemplate() irá procurar pelo objeto hibernateTemplate na classe pai – o objeto não será encontrado visto que não foi injetado – e você terá um erro do tipo “java.lang.IllegalArgumentException: sessionFactory or hibernateTemplate is required”. A causa desse erro é bem difícil de sacar, mas você pode consertar bem facilmente também. Como a sua classe Dao precisa de um hibernateTemplate ou sessionFactory e não os encontra na classe pai, você mesmo precisa encontrar uma forma de injetá-los. Eu fiz assim:


@Repository("horarioDao")
public class HorarioDao extends Dao<Horario> implements IHorarioDao {

    @Autowired //injeção via set
    public HorarioDao(SessionFactory sessionFactory) {
        super(Horario.class);
        setSessionFactory(sessionFactory);
    }
}

Nesse caso, a classe Dao estende HibernateDaoSupport, causando o conflito mencionado. A solução: criar um construtor que receba a SessionFactory que você carrega uma vez no seu contexto de dados como parâmetro e marcar o construtor como @Autowired para que a injeção seja feita dinamicamente pelo container. Além disso, dentro do construtor invoque o setSessionFactory(que é herdado) passando o objeto. Voilá! Problema contornado!

Conclusão

Martin Fowler diz que bom código não é aquele que computadores entendem, mas sim aqueles que humanos possam entender. Assim, anotando seus atributos/métodos/construtores a serem injetados você está facilitando a vida de quem irá ler o código futuramente, tornando o aprendizado do seu código mais rápido. XML´s são ótimos, não me entenda mal. Mas você deve se preocupar se começa a existir uma dependência grande de xml no seu projeto e as configurações são gigantescas.

Claro, conflitos ocorrem, como você pôde notar no caso do HibernateDaoSupport, mas a técnica de contorná-lo é bem esperta também! De qualquer forma, o uso do HibernateDaoSupport é desencorajado oficialmente pelo Spring desde a versão 3.0. Mas esse é um assunto pra outro post…

Padrão