terça-feira, 6 de maio de 2014

Recursividade

  Estava assistindo uma aula de Scala e em um dos exercícios tinhamos que implementar um método para descobrir a soma de uma lista de inteiros, porém o exercicio dava a sugestão de usar recursividade ao invés de fazer um loop.
  Neste post vou mostrar o raciocinio que utilizei para resolver o exercicio e mostrar como eles seriam resolvidos passo a passo em Java.

  Mas antes um pouco de teoria:
  • Para entender recursividade, você precisa entender recursividade.
  • PHP é um acronimo recursivo que significa: PHP: Hypertext Processor
  • WINE é outro exemplo de recursividade, a sigla significa: Wine Is Not an Emulator
  • outro exemplo é GNU que significa: Gnu's Not Unix
Deu pra ver onde quero chegar? em todos os casos acima você acaba criando um laço de repetição, pra entender recursividade, você tem que entender recursividade, então vá entender recursividade, pra isso você tem que entender recursividade... Ok parei.
Parece complicado, mas  na verdade é simples. Recursividade nada mais é do que chamar o metodo que está sendo declarado dentro de seu próprio corpo. Como exemplo, temos um metodo para ver o N numero da sequencia de Fibonacci:

    public static long fibonacci (int i) {
        if (i == 1) return 0;
        if (i == 2) return 1;

        long numero = fibonacci(i - 1) + fibonacci(i - 2);
        return numero;
    }

Olhos atentos perceberam que o metodo fibonacci, chama o metodo fibonacci logo antes de dar o return, isso é chamado de metodo recursivo.
  O que ocorre ali é o seguinte: A sequencia de fibonacci define que
  • primeiro termo da sequencia é 0
  • o segundo termo da sequencia é 1
  • a partir daí, o termo n é igual ao termo n-1 somado do termo n-2
Viu como é simples enxergar estas regras no codigo acima? Na hora que o metodo fosse executado com o argumento 5, ele se expandiria, e aconteceria algo +- assim:
fibonacci(5) = fibonacci(4) + fibonacci(3)

sabemos que fibonacci de 4 é igual a fibonacci de 3 + fibonacci de 2 então:

fibonacci(5) = fibonacci(3) + fibonacci(2) + fibonacci(3)

sabemos que fibonacci de 3 é igual a fibonacci de 2 + fibonacci de 1 se substituirmos isso na nossa igualdade temos:

fibonacci(5) = fibonacci(2) + fibonacci(1) + fibonacci(2) + fibonacci(2) + fibonacci(1)

pela definição da sequencia de fibonacci, sabemos que fibonacci(2) = 1 e fibonacci(1) = 0. subsitituindo isto na igualdade temos:

fibonacci(5) = 1 + 0 + 1 + 1 + 0
fibonacci(5) = 3

Pronto. Recursivamente descobrimos que o quinto termo da sequencia de fibonacci é 3, o que bate com a sequencia mostrada na wikipedia (linkada ali em cima)

Agora vamos implementar o método para descobrir a soma da lista.
Se fossemos utilizar a solução com o loop, seria mais ou menos assim:


    public static int somaLista(ArrayList<Integer> lista){
        int total=0;
        for (int i=0; i < lista.size(); i++){
            total = total + lista.get(i);
        }
        return total;
    }

Agora vamos pensar recursivamente:
primeiro precisamos definir uma regra para nos tirar do loop, assim, alguma hora vamos parar de invocar o método. Queremos somar os elementos da lista, então, se a lista estiver vazia, a soma será 0. Logo nossa regra é:
  • Se a lista estiver vazia, o resultado da soma é zero
Para que nossa regra nos ajude, a lista deverá diminuir a cada chamada do método, assim uma hora ela ficará vazia e terminaremos nossa execução. Então na hora de implementar o código vamos remover o primeiro elemento da lista, e somar com o resto da lista. O código fica assim:

    public static int somaListaRecursivo(List<Integer> lista){
                
        if (lista.isEmpty()) return 0;
        
        int aux=lista.get(0);
        lista.remove(0);
        
        return aux + somaListaRecursivo(lista);
    }

Ao executarmos o código

    public static void main(String[] args) {
        ArrayList<Integer> l = new ArrayList<>();
        l.add(2);
        l.add(3);
        l.add(3);
        System.out.println(somaListaRecursivo(l));
    }

temos a saida 8.

Como sempre, se tiver alguma dúvida, sugestão ou critica, deixe nos comentarios.

sábado, 26 de abril de 2014

Construtores



   Os construtores são utilizados para inicializar uma classe, apesar de serem simples de se utilizar e entender, algumas pessoas tem dúvidas e/ou acabam utilizando sem saber sobre sua existencia, com este post eu pretendo mostrar o conceito de construtor com um exemplo prático. A linguagem utilizada será o Java, os construtores serão utilizados para inicializar um JFrame já com algumas informações da nossa entidade.


Sobre a sintaxe dos construtores:
  • Construtores DEVEM ter o exato nome da classe, lembrando que Java é case sensitive
  • Construtores NUNCA terão retorno, nem mesmo void. Se for colocado algum tipo de retorno ele deixará de ser um construtor e se tornará um metodo normal.
  • Construtores podem ter qualquer visibilidade (public, default, protected ou private) porem construtores private podem dificultar herança (veja a seguir)
 Algumas coisas que você deve saber sobre construtores:
  • Usa-se o comando this() para chamar um construtor sem parametros
  • Usa-se o comando this("construtor") para chamar um construtor que pede uma String como parametro
  • Usa-se o comando super() para chamar o construtor da classe pai que não pede nenhum parâmetro
  • Usa-se o comando super("construtor pai") para chamar o construtor da classe pai que pede uma String como parâmetro.
  • Se você não definir um construtor, será criado um construtor padrão pela JVM: um construtor sem parametros e que apenas chama super()
  •  Todo construtor, por padrão, chama o construtor sem parâmetros da classe pai, então mesmo que você escreva código para isso, está implicito que na primeira linha depois de sua declaração existe o comando super(). O único jeito de alterar isto é você declarar explicitamente a chamada de algum outro consstrutor com this ou super.
  • Pelo item anterior, podemos observar que quando uma classe é instanciada, pelo menos um construtor de cada nivel da hierarquia é chamado até chegarmos no construtor da classe Object (pois nossas classes implicitamente herdam de Object)
  • Se você definir apenas um construtor com visibilidade private, sua classe não poderá ser instanciada fora da própria classe, isto é utilizado no padrão Singleton por exemplo.
A seguir está um exemplo simples de utilização de construtores:
 
  Primeiro eu criei um POJO de cliente:
public class Cliente {
    private String nome;
    private String tel;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }
} 
  Nada de novo por aqui, uma classe de entidade com dois atributos, 2 getters e 2 setters.
    
  Depois eu criei um JFrame, este JFrame tem dois campos de texto, um para exibir o nome do cliente e outro para exibir o telefone:

  
public class ClienteFrame extends JFrame{
    
    private JTextField txtNome = new JTextField();
    private JTextField txtTel = new JTextField();

    public ClienteFrame() {
        this.setLayout(new GridLayout(4, 1));
        this.add(new JLabel("Nome"));
        this.add(txtNome);
        this.add(new JLabel("Telefone"));
        this.add(txtTel);
        this.setSize(300, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public ClienteFrame(Cliente c){
        this();
        this.txtNome.setText(c.getNome());
        this.txtTel.setText(c.getTel());
    }    
    
}
  O JFrame tem dois atributos privados, que são os campos de texto que exibirão as informações. Além destes atributos, a classe ClienteFrame tem dois “métodos” que tem o mesmo nome da classe, estes são os construtores desta classe. O construtor sem argumentos monta o layout no nosso frame, enquanto, o construtor que pede um argumento chama o construtor sem argumentos (para que o layout seja montado) e depois adiciona as informações que recebeu como parametro.

SEMPRE que uma classe é instanciada, seu construtor é chamado. Perceba:
JTextField txt = new JTextField();
Cliente c = new Cliente(); //outro construtor
JTextField txt2 = new JTextField("caixa com texto"); //construtor para inicializar o campo contendo um texto

Para finalizar, vamos instanciar nossa classe ClienteFrame com os dois construtores e ver a diferença:
primeiro instanciamos o Frame sem passar nenhum argumento:

public class Main {
    public static void main (String[] args){
        ClienteFrame frame = new ClienteFrame();
        frame.setVisible(true);
    }
}

o resultado é um frame com os campos em branco, como mostra a figura:



Mas podemos também instanciar nosso frame passando um objeto do tipo Cliente como parâmetro:

public class Main {
    public static void main (String[] args){
        //criamos o objeto parametro:
        Cliente c = new Cliente();
        c.setNome("Nome do cliente");
        c.setTel("2673-9000");
        //passamos o objeto criado para o nosso frame:
        ClienteFrame frame = new ClienteFrame(c);
        frame.setVisible(true);
    }
}

O resultado será:


No momento acredito que isto seja o básico, se tiver qualquer dúvida sobre o assunto deixe nos comentários

quinta-feira, 3 de abril de 2014

Usando triggers para criar logs no MySQL


Há um tempo atrás onde trabalho, surgiu a necessidade de saber quais informações eram inseridas eou alteradas e quando estas alterações ocorriam. O sistema que realizava estas alterações tinha um código extremamente mal escrito, então quanto menos alterações fossem feitas no código, melhor. A solução a que cheguei foi utilizar triggers nas tabelas que necessitavam destes logs.
O tutorial utiliza o MySQL, porém a sintaxe para outros SGBD's é bem similar (se não for igual)
Primeiro temos a tabela que desejamos criar os logs:
create table venda (  
      id int auto_increment primary key,  
      dia datetime,  
      valor float  
);
Até aqui sem novidades, este é apenas o código para criar a hipotética tabela de venda, é nesta tabela onde estão as informações q desejaremos “logar”.
O próximo passo é criar a tabela que armazenará as informações:
create table venda_log (  
      log_id int auto_increment primary key,  
      old_id int,  
      old_dia datetime,  
      old_valor float,  
      new_id int,  
      new_dia datetime,  
      new_valor int,  
      acao varchar(6),  
      usuario varchar(50),  
      hora timestamp  
 );  
Nesta tabela temos um campo de id para chave primaria do log, dois campos para cada campo da tabela Venda, um com o prefixo old e outro com o prefixo new. Temos o campo acao para sabermos o que foi feito na tabela, o campo usuario para sabermos qual usuario (do mysql) fez a alteração e o campo hora para sabermos quando esta alteração foi feita.
Agora temos que criar as triggers que alimentarão esta tabela:
create trigger venda_log_insert after insert on venda  
 for each row  
           insert into venda_log(old_id, old_dia, old_valor, new_id, new_dia, new_valor, acao, usuario, hora)   
                     values(null,null,null, new.id, new.dia, new.valor, 'INSERT', user(), now());  
Se você nunca criou uma trigger pode estranhar o código acima, mas é mais simples do que parece:
  • create trigger = código para criar a trigger.
  • venda_log_insert = nome da triger.
  • after = tempo de execução da trigger. Before para executar a trigger antes do comando que causou seu disparo e after para executar a trigger depois do comando que causou seu disparo.
  • insert = o evento que dispara a trigger, poderia ser por exemplo update ou delete
  • venda = a tabela a que a trigger está associada. Esta trigger por exemplo, será disparada quando acontecer um insert na tabela venda.
  • for each row = indica que o corpo da trigger começou, o que vem depois disso é o que de fato a trigger faz.

Outro detalhe importante sobre a trigger é a utilização de “new”, dentro da trigger temos variaveis que representam os campos (da tabela indicada depois do on) antes e depois da alteração. Para utilizarmos o campo antes, usamos old.nome_do_campo e para o campo depois da alteração usamos new.nome_do_campo.
Em triggers de insert, não temos os campos old pois antes da execução do comando a linha ainda não existe. Em triggers de delete não temos os campos new pois não existe linha depois da execução do comando e em triggers do tipo update temos os dois tipos de campo.
O que esta trigger faz então é simples, pega os valores que foram inseridos na tabela indicada e loga eles em uma tabela, informando também o usuario e a hora do acontecimento. Para deixarmos o log completo vamos criar uma trigger para delete e uma para update:
 create trigger venda_log_update after update on venda  
 for each row  
      insert into venda_log(old_id, old_dia, old_valor, new_id, new_dia, new_valor, acao, usuario, hora)   
                values(old.id, old.dia ,old.valor, new.id, new.dia, new.valor, 'UPDATE', user(), now());  
 create trigger venda_log_delete after delete on venda  
 for each row  
      insert into venda_log(old_id, old_dia, old_valor, new_id, new_dia, new_valor, acao, usuario, hora)   
                values(old.id, old.dia ,old.valor, null, null, null, 'DELETE', user(), now());  

Os logs estão prontos, para testar vamos realizar algumas operações na nossa tabela:

 insert into venda (dia, valor)values ('2014/03/09 19:43:00',137.00);  
 update venda set valor = 777 where id = 1;  
 delete from venda where id = 1;  

Ao executarmos um select na nossa tabela de log temos 3 linhas:

A primeira linha nos mostra que as 19:45 foi feito um insert, os campos com prefixo new dizem quais valores foram inseridos. A segunda linha nos diz que às 19:49 os valores de uma venda foram alteradas, o valor antigo da venda era 137 e o novo valor passou a ser 777. A ultima linha mostra que havia uma venda com os valores mostrados no old, mas ela foi deletada.
Esta é apenas uma das maneiras de se criar um log em seu banco de dados, se tiver sugestões, críticas, opiniões ou quaisquer pensamentos aleatórios, comente ^^