terça-feira, 26 de março de 2013

Encapsular campos em Python 3.x

Encapsulamento de dados é um recurso presente em linguagens que seguem o paradigma orientado a objetos. Esse recurso tem como objetivo restrigir o acesso direto a informação, ou seja, ele protege informações que não devem ser alteradas em qualquer parte do programa. Com isso podemos entender que quem está de fora apenas sabe que o serviço é realizado mas não sabe como o serviço é implementado.

Certamente quando começamos a programar orientado a objetos não vemos os benefícios que o encapsulamento de dados nos proporciona, pois, a primeira vista ele só serve para gerar mais linhas de código.

Hoje vou falar como encapsular dados na linguagem de programação Python versão 3.x. Se você já programou em alguma linguagem .Net (Visual Basic, C#) não sentirá grandes mudanças, porém, se você é um programador Java sentirá uma certa estranheza quando ler o código.

Bom sem mais demora vamos conhecer mais um pouquinho da linguagem Python e sua implementação diferente em POO.

Protegendo minhas variáveis dos Noobs

Na minha opinião o encapsulamento de dados existe, porém, não garante a total segurança do seu código. Antes de você me chamar de louco vou explicar, se você é um programador Java e ficou muito ofendido com o que eu disse ... me desculpe essa não foi a intenssão! Mas Sr.Programador Java, seu código não está seguro só porque o Sr. usa o modificador de acesso private em suas variáveis de instância, se alguém quiser saber quais são as variáveis presentes em determinadas classes de seu sistema ele pode fazer isso utilizando a própria API do Java a nossa querida Reflection. Logicamente que trabalhar com essa API não é algo trivial, porém, pense duas vezes antes de desafiar um nerd dizendo que seu código está 100% seguro.

Em Python a história também não é diferente, se alguém quiser saber quais variáveis sua classe tem essa pessoa vai acabar descobrindo de um jeito ou de outro. Por isso que a ideia principal de Python é que tudo seja public e somente o necessário, seja private. Muitas vezes quando estamos aprendendo POO o pessoal costuma dizer: "Grande parte das vezes sua classe terá atributos de instância privado e métodos públicos". Mais nem sempre é assim, podemos realmente ter variáveis em nossas classes que não precisam serem private.

Bom, se você está certo que precisa ter variáveis private em sua classe e não quer que ela seja acessível fora da classe onde ela foi especificada, então você terá que informar primeiramente que ela será private, para que o interpretador Python saiba disso. Para dizer que um atributo de instância é private em python utilizamos dois underline antes do nome da variável. Exemplo:

class Gato:
    __raca = None
    ...

Com isso dizemos ao interpretador que o atributo raça é privado. Agora temos que construir dois métodos, um para setar (setter) e outro para recuperar (getter) o valor do atributo. No python 3.x foi introduzida uma sintaxe muito elegante para realizar esse serviço. Veja:

class Gato:
    def __init__(self):
        __raca = None

    @property
    def raca(self):
        return __raca

    @raca.setter
    def raca(self, value):
        self.__raca = value

Viu como é simples? Bom o que fizemos foi definir dois métodos (em python pode-se falar função da classe), onde no primeiro definimos o getter e o segundo o definimos o setter. Observe que os dois métodos tem o mesmo nome, mas possuem anotações diferentes. A primeira anotação @property indica o método é um getter e a segunda anotação @raca.setter indica que o método é um setter,  perceba que a segunda anotação é bem diferente da primeira, nela devemos informar o nome do atributo seguido de ponto e a palavra setter.

Podemos definir que um atributo seja apenas read-only. Para fazer isso basta excluir o método setter. Veja:

class Gato:
    def __init__(self):
        __raca = None

    @property
    def raca(self):
        return __raca

Agora observe como fica natural o acesso aos atributos:

if __name__ == '__main__':
    mimo = Gato()
    megui = Gato()

    mimo.raca = 'angorá turco'
    mimo.idade = 2

    megui.raca = 'siames'
    megui.idade = 1

    print('Megui é da raça {}, e tem {} ano(s) de idade.'.format(megui.raca, megui.idade))
    print('Mimo é da raça {}, e tem {} ano(s) de idade.'.format(mimo.raca, mimo.idade))

Pessoal o post se encerra por aqui! Espero que tenham gostado de aprender um pouco mais sobre Python e veja vocês em um próximo post, até lá e BONS ESTUDOS!

terça-feira, 19 de março de 2013

MinhaString & MinhaData

Fala pessoal, se você acompanha o blog e quando lê esses posts que não explicam absolutamente nada e que apenas possuem códigos e enunciados saiba que eles fazem parte das listas de exercícios da faculdade que eu curso (Instituto Federal de Educação, Ciência e Tecnologia de São Paulo - campus Araraquara). Bom hoje vou postar mais um  desses exercícios, porém, leia o código copie e execute ai na sua máquina, aproveite para brincar e descobrir o que ele faz ... isso te dará um conhecimento extra que muitas vezes deixamos escapar em uma explicação mais formal. Beleza?!

Enunciados:


A classe MinhaData está muito simples, implemente as seguintes melhorias:
  • Não permitir que uma data inválida seja instanciada. Caso tente-se instanciar uma data inválida instancie 01/01/1900.
  • Implemente um construtor que aceite várias ordens de argumentos parainstanciar uma data. Por exemplo, o mesmo construtor deve receber osseguintes dados: (1, 2, 1900) ou (1900, 2, 1) e instanciar a data 01/02/1900.
  • Implementar um método que retorne a data por extenso.
  • Implementar um método que retorne a quantidade de dias entre a data instanciada e uma data passada como argumento.
A classe MinhaString:
  • Sabe-se que em Java ou mesmo em C não existe um tipo primitivo para armazenar uma String. Implemente a classe MinhaString que permita manipular um conjunto de até 256 caracteres. Implemente todas as funcionalidades que acharem interessante para esta classe, como por exemplo: converter para maiúsculo, inverter o texto, comparar dois textos.

MinhaData.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package logica;

/**
 *
 * @author guilherme
 */
public class MinhaData {
    private int dia;
    private int mes;
    private int ano;

    public MinhaData(int param1, int param2, int param3, String mascara) {
        organizaValores(mascara, param1, param2, param3);
    }

    private void confereData(int dia, int mes, int ano) {
        boolean dataValida = true;
     
         /* Verifica ano */
        if(ano < 0) {
            dataValida = false;
        } else {
            this.ano = ano;
        }
     
        if(mes < 0 || mes > 12) {
            dataValida = false;
        } else {
            this.mes = mes;
        }
     
        /* Verifica se o dia é válido */
        if(dia > 1 && dia < 31) { //verifica se o dia não é negaivo
            if(mes == 2) {
                if(dia < 28 || (dia == 29 && anoBissexto(this.ano))) {
                    this.dia = dia;
                } else {
                    dataValida = false;
                }
            } else if(mes == 1 || mes == 3 || mes == 5 || mes == 7 || mes == 8
                    || mes == 10 || mes == 12) {
                    this.dia = dia;
            } else {
                if(dia > 30) {
                    dataValida = false;
                }
                this.dia = dia;
            }
        } else {
            dataValida = false;
        }
     
        /* Se a data não for válida então aplica-se a data padrão */
        if(!dataValida) {
            this.dia = 1;
            this.mes = 1;
            this.ano = 1990;
        }
     
    }
 
    private boolean anoBissexto(int ano) {
        if(ano % 4 == 0 && (ano % 100 != 0 || ano % 400 == 0)) {
            return true;
        }
        return false;
    }
 
    public String imprimeData(String padrao) {
        switch(padrao.toLowerCase()) {
            case "pt-br":
                return dia + "/" + mes + "/" + ano;
            case "us":
                return ano + "-" + mes + "-" + dia;
            default:
                return null;
        }
    }

    private void organizaValores(String mascara, int param1, int param2, int param3) {
        switch(mascara) {
            case "ymd":
                confereData(param3, param2, param1);
                break;
            case "ydm":
                confereData(param3, param1, param2);
                break;
            case "myd":
                confereData(param2, param3, param1);
                break;
            case "mdy":
                confereData(param2, param1, param3);
                break;
            case "dmy":
                confereData(param1, param2, param3);
                break;
            case "dym":
                confereData(param1, param3, param2);
                break;
            default:
                confereData(0, 0, 0);
                System.out.println("Esse formato não existe!");
        }
    }

    public String dataPorExtenso() {
        String[] meses = {"janeiro", "fevereiro", "março", "abril", "maio", "junho"
        ,"julho", "agosto", "setembro", "outubro", "novembro", "dezembro"};
     
        return String.valueOf(dia) + " de " + meses[mes - 1] + " de " + String.valueOf(ano);
    }
 
    public double qtdeDeDias(MinhaData data1, MinhaData data2) {
     
        int[] nroDiasMes = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        int dias = 0;
 
        for(int i=0; i<data1.ano; i++) {
            if(anoBissexto(i)) {
                dias += 366;
            } else {
                dias += 365;
            }
        }
     
        for(int i=0; i<data1.mes; i++) {
            dias += nroDiasMes[i];
        }
     
        for(int i=0; i<data1.dia; i++) {
            dias += 1;
        }
     
        for(int i=0; i<data2.ano; i++) {
            if(anoBissexto(i)) {
                dias -= 366;
            } else {
                dias -= 365;
            }
        }
     
        for(int i=0; i<data2.mes; i++) {
            dias -= nroDiasMes[i];
        }
     
        for(int i=0; i<data2.dia; i++) {
            dias -= 1;
        }
     
     
        return Math.abs(dias);
    }

}


MinhaString.java
package logica;

/**
 *
 * @author guilherme
 */
public class MinhaString {
    char[] str;

    public MinhaString(char[] str) {
        this.str = str;
    }
   
    public int tamanho(char[] str) {
        int tamanho = 0;
        for(char c : str) {
            ++tamanho;
        }
        return tamanho;
    }
 
    public char[] concatena(char[] str) {
        int tam = tamanho(str) + tamanho(this.str);
        char[] aux = new char[tam];
     
        for(int i = 0; i < tamanho(this.str); i++) {
            aux[i] = this.str[i];
        }
     
        for(int i = tamanho(this.str), j = 0; i < tam; i++, j++) {
            aux[i] = str[j];
        }
     
        this.str = aux;
     
        return this.str;
    }
 
    public char[] maiuscula() {
        char[] aux = str;
        for(int i=0; i < tamanho(aux); i++) {
            if(str[i] > 96 && str[i] < 123) {
                str[i] -=32;
            }
        }
        return this.str;
    }
 
    public char[] minuscula() {
        char[] aux = str;
        for(int i=0; i < tamanho(aux); i++) {
            if(str[i] > 64 && str[i] < 91) {
                str[i] +=32;
            }
        }
        return this.str;
    }
 
    public char[] inverte() {
        int tam = tamanho(str);
        char[] x = espelha(str);
        for(int i=tam-1, j=0; i > -1; i--, j++) {
            str[j] = x[i];
        }
        return this.str;
    }
 
    public char[] trocarCaracter(char caracterAntigo, char caracterNovo) {
        char[] aux = espelha(str);
        for(int i=0; i<tamanho(aux); i++) {
            if(str[i] == caracterAntigo) {
                str[i] = caracterNovo;
            }
        }
        return str;
    }
 
    public char[] subString(int x, int y) {
        int tam = tamanho(str);
        if( (x >= 0 && x <= tam) && (y >= 0 && y <= tam) ) {
         
            char[] aux = new char[y - x];
         
            for(int i=x, j=0; i<y; i++, j++) {
                aux[j] = this.str[i];
            }
         
            return aux;
        }
        return this.str;
    }
 
    public int comparar(char[] str1, char[] str2) {
        /*
         *  0  são iguais
         *  1  maior
         * -1  menor
         */
     
        int t1 = tamanho(str1);
        int t2 = tamanho(str2);
     
        if(t1 > t2) {return 1;} else if(t2 > t1) {return -1;}
        else {
            for(int i=0; i<t1; i++) {
                if(str1[i] > str2[i]) {
                    return 1;
                } else if(str2[i] > str1[i]) {
                    return -1;
                }
            }
            return 0;
        }
    }
 
    public void valueOf(char[] str) {
        for (char c : str) {
            System.out.print(c);
        }
    }
 
    private char[] espelha(char[] str) {
        int tam = tamanho(str);
        char[] x = new char[tam];
        for(int i=0; i<tamanho(str); i++) {
            x[i] = str[i];
        }
        return x;
    }
 
}
Bom pessoal postei para ajudar e não para copiar, a intenção é evolução e não regressão beleza!? Bons estudos!

MinhaData versão Python

Fala pessoal, estava de boa aqui em casa e resolvi programar um pouco em Python. Implementei a classe MinhaData que originalmente foi escrita em Java.
Quando executei descobri um agravante, o código em Java estava cortando o resultado quando as datas passadas eram muito distantes. Já em Python não tive esse problema e a execução do código também se mostrou mais rápida ... o que o Java fazia em x minutos Python fazia em x/2.
Vamos ver como ficou? Segue o código ai:

MinhaData.py
#-*- encoding: utf-8 -*- import timeit class MinhaData(): def __init__(self, dia, mes, ano): self.dia = dia self.mes = mes self.ano = ano def isBissexto(self, ano): if ano % 4 == 0 and (ano % 100 != 0 or ano % 400 == 0): return True return False def calcDias(self, d1, d2): return abs(self.qtdeDias(d1) - self.qtdeDias(d2)) def qtdeDias(self, data): nroDiasMes = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] dias = 0 for dia in range(1, data.dia+1): dias += 1 for mes in range(data.mes): dias += nroDiasMes[mes] for ano in range(1, data.ano+1): if self.isBissexto(ano): dias += 366 else: dias += 365 return dias if __name__ == '__main__': d1 = MinhaData(21,03,2013) d2 = MinhaData(21,03,1994) print('Intervalo de',d1.calcDias(d1, d2), 'dia(s)')

Pessoal estou preparando um post juntamente com o Guilherme Bueno onde comparamos o desempenho das linguagens de programação: Java, C/C++ e Python executando um mesmo algoritmo ... aguadem. Bons estudos!!!

terça-feira, 12 de março de 2013

Realizando leitura com System.in.read

Fala pessoal, hoje vou mostrar como realizar a leitura do teclado utilizando System.in.read(). Então teremos hoje mais um post dedicado aos programadores Java e ao pessoal que gosta de Java. Gostaria de alertar que a partir do Java 5 a leitura do teclado é feita com a classe Scanner do pacote java.util.Scanner.

Realizando leitura do teclado sem utilizar a classe Scanner

Como dito anteriormente nas versões anteriores da Java 5 a leitura do teclado exigia um certo esforço do programador. Antigamente o programador utilizava o método System.in.read() que retorna um InputStream bufferizado. Então o programador pegava este buffer retornado e convertia-o para caracter para que ele pudesse converte-lo para os demais formatos.

Exemplos práticos

Vou utilizar dois problemas apresentados na aula de POO do IFSP campus Araraquara curso ADS para ilustrar como é feita a leitura utilizando a classe System.in.read().
  1. Construa um programa Java que leia o ano de nascimento do usuário e calcule a idade do mesmo;
  2. Construa um programa Java que leia uma sigla para sexo (M/F) e depois apresente no console o sexo lido;
    Atenção: A leitura do teclado deve ser realizada com o método System.in.read();

SysRead2.java
import java.io.IOException; public class SysRead2 { public static void main(String[] args) throws IOException { byte[] aux = new byte[4]; System.out.println("Informe seu ano de nascimento: "); System.in.read(aux); int anoNascimento = Integer.parseInt(new String(aux, "UTF-8")); System.out.println("Idade: " + (2013 - anoNascimento)); } }

SysRead3.java
import java.io.*; public class SysRead3 { public static void main(String[] args) throws IOException { byte sexo; System.out.println("Informe o sexo M/F: "); sexo = (byte) System.in.read(); System.out.println("Sexo digitado: " + (char)sexo); } }

Dúvidas e sugestões deixem comentários, Bons estudos.

domingo, 3 de março de 2013

Porque não utilizar o DefaultTableModel

Olá pessoal, hoje vamos falar de TableModel e porque não devemos utilizar o DefaultTableModel em sua JTable.
Para que o leitor compreenda o assunto tratado é necessário que o mesmo tenha conhecimento dos princípios básicos de POO e que conheça o funcionamento do toolkit para construções gráficas swing.

Nem sempre o modelo pronto é o mais fácil

Muitas vezes quando estamos trabalhando com JTable não nos preocupamos em implementar o modelo da Table e simplemente utilizamos o modelo padrão (DefaultTableModel). O DefaultTableModel é um modelo padrão utilizado para que o programador possa adaptá-lo a suas necessidades. Mas nem sempre adaptar esse TableModel padrão é uma boa escolha, na maioria das vezes o DefaultTableModel deixará o desempenho da aplicação comprometido assim como a segurança da mesma.

A situação pode ficar ainda pior, vamos supor que você tenha feito um software a 3 anos atrás e nesse software você persistio as informações com JDBC, porém, agora você deseja migrar para um ORM de sua proferencia, por exemplo Hibernate. Você terá que percorrer linha por linha da JTable e mapear as informações para objetos e depois montar um List para então poder salvar os dados contidos em sua JTable.

Por esse e outros motivos a melhor solução seria a implementação de um TableModel para cada JTable de sua aplicação e abolir de vez o uso do DefaultTableModel.

Implementando um TableModel simples

Sempre quando procuramos ajuda na Internet o pessoal escracha o DefaultTableModel e manda a pessoa implementar um TableModel e se quer diz como a pessoa deve fazer.

Veja a implementação de um TableModel para sua JTable:


public class TableModelOrdemServico extends AbstractTableModel { private String[] colunas = new String[]{"Descrição", "Qtde"};//aqui vc coloca os nomes que iram aparecer na coluna de sua JTable, o número de colunas é igual ao número de elementos que contem no Array private Class[] classeColuna = new Class[]{String.class, Integer.class};//aqui vc coloca o tipo de dado que cada coluna armazena. Os tipos suportados são: Integer, Double, String e Object. private List linhas;//Cada elemento do List representa uma linha da JTable, então quando um novo elemento é adicionado na tabela ele também é adicionado no List. public TableModelOrdemServico() { linhas = new ArrayList<>(); } public TableModelOrdemServico(List listaDeItensDeVenda) { this(); linhas.addAll(listaDeItensDeVenda); } @Override public String getColumnName(int columnIndex) {//Retorna o nome da coluna return colunas[columnIndex]; } @Override public int getColumnCount() {//Retorna o número de colunas que seu modelo tem. Repare que o número de colunas é igual ou tamanho do Array. return colunas.length; } @Override public int getRowCount() {//Retorna o número de linhas que o modelo da tabela tem. Repare que o número de elementos corresponde ao número de linhas do modelo return linhas.size(); } @Override public boolean isCellEditable(int rowIndex, int columnIndex) {//Retorna qual célula da tabela é editável, nesse exemplo nenhuma célula é editável return true; } @Override public Class getColumnClass(int columnIndex) {//Retorna a classe da coluna selecionada return classeColuna[columnIndex]; } @Override public Object getValueAt(int rowIndex, int columnIndex) {//Retorna o valor contido da célula OrdemServico o = linhas.get(rowIndex); switch(columnIndex) { case 0: return o.getDescricao(); case 1: return o.getQtde(); default: return null; } } @Override public void setValueAt(Object obj, int rowIndex, int columnIndex) {//Modifica/Atualiza o valor contido na célula. OrdemServico o = linhas.get(rowIndex); switch(columnIndex) { case 0: o.setDescricao(obj.toString()); break; case 1: o.setQtde(Integer.parseInt(obj.toString())); break; case 2: o.setQtde(Integer.parseInt(obj.toString())); break; } fireTableDataChanged(); } //Os métodos listados acima devem ser reescritos em cada classe que extenda AbstractTableModel (se vc não entende o que a palavra extends representa em Java leia sobre Herança aqui no Blog temos um POST explicando sobre o assunto). //Você ainda pode implementar quantos métodos forem necessários para sua aplicação

Espero que tenham gostado e até a próxima pessoal.

Novidade!!! Agora vamos ter canal no Youtube =D

Fala pessoal tudo beleza, estou sumido a correria está forte por aqui. Estou querendo dar um start em um projeto antigo que vem desde o temp...