Entendendo orientação a objetos, parte 1

Hoje venho iniciar uma série para demonstrar o conceito de orientação a objetos dentro do mundo da programação. A POO (Programação Orientada a Objetos) foi implementanda a fim de suprir algumas deficiências do principal modelo anterior, a programação estruturada. Atualmente temos as linguagens C++ e Java como sendo as mais conhecidas no mundo da POO, entre outras que estão ganhando cada vez mais força, como python, ruby e scala, por exemplo, neste artigo teremos uma pequena direcionalização ao Java, porém todos os conceitos aqui apresentados podem ser facilmente adaptados a qualquer linguagem de programação que utilize POO.

Para começarmos com o conceito de POO vamos fazer uma pequena analogia entre programação estruturada e POO, na programação estruturada criamos nossos algoritmos no estilo “passos para executar uma receita de bolo”, você descreve passo a passo todas as operações que devem ser realizadas até que o bolo esteja pronto, já no estilo POO nós temos um paradigma um pouco mais completo, ainda considerando o exemplo do bolo, na POO teríamos um conjunto de objetos que podem ou não ter uma capacidade de executar tarefas, e com a reunião de todos os objetos conseguiríamos preparar o bolo.

A primeira coisa a qual precisa ser desmistificada na POO é o conceito de classe. Explicando da forma mais simples possível uma classe é um conjunto de atributos e métodos, mas o que são atributos e métodos? Os atributos de uma classe são as características que esta pode assumir, suas personificações. Os métodos são semelhantes às funções comuns que estamos acostumados na programação estruturada, com a diferença que os métodos são funções particulares de cada classe, ou seja, um método é uma operação que determinada classe é capaz de executar.

Vamos pensar em exemplos para facilitar o compreendimento do que é uma classe. Podemos assumir no mundo da POO que uma pessoa é uma classe, se uma pessoa é uma classe e que esta classe tem alguma utilidade então esta deve possuir pelo menos um atributo ou um método, vamos analisar o que pode ser atributo e o que pode ser método na classe pessoa. Vamos pensar, quais são as caraterísticas de uma pessoa? Tente descrever as características de você mesmo: meio alto, magro, cabelos pretos, inteligente, rápido, todas estas características são consideradas atributos de uma pessoa, então temos, neste caso, que pessoa é uma classe, e se esta pessoa possui as características descritas anteriormente podemos assumir que estas características são os atributos da classe pessoa. E o que seria os métodos de uma classe? Um método seria a capacidade que esta classe tem de interagir com o seu ambiente. Voltamos a classe pessoa, quais seriam as capacidades de uma pessoa interagir com o seu ambiente? Falar, andar, pular, gesticular, etc.

OBS.: Em geral, os métodos são as ações de uma classe, ou seja, métodos no mundo da gramática são os verbos, por exemplo: conversar, andar, pular, tocar, etc. Os atributos poderiam ser considerados os adjetivos, por exemplo: feio, fraco, chato, lerdo, etc.

É muito importante conseguir identificiar o que pode ser uma classe e conseqüentemente seus atributos e seus métodos. Um exercício muito indicado é procurar “coisas” na natureza e fazê-las como classe, a partir daí identificar seus atributos, exemplo: Caixa de som, animal, carro, árvore, computador, etc.

Acesso simples e rápido às linhas de uma query

Abaixo segue duas classes para encapsular e facilitar o acesso a banco de dados via JDBC, a classe DBQuery encapsula uma query onde a mesma é capaz de gerar um iterator para navegação nas linhas da consulta de forma rápida, leve e eficiente.

O iterator é implementado de uma forma em que ele não guarde os dados da consulta em memória, possibilitando o uso dessa classe para queries que retornem muitas linhas de resultado.

Outra coisa interessante é a possibilidade de se trabalhar com genéricos, caso sua consulta ao banco retorne um tipo homogêneo de dados (por exemplo, todos os campos sejam do tipo String).

classe DBQuery.java

/**
 *
 * @author Paulo Canedo C Rodrigues
 */
public class DBQuery<t> implements Iterable</t><t> {
 
    private Connection connection;
    private String query;
 
    public DBQuery(Connection connection, String query) {
        this.connection = connection;
        this.query = query;
    }
 
    public Iterator</t><t> iterator() {
        try {
            Statement stm = connection.createStatement();
            ResultSet rs = stm.executeQuery(query);
            return new DBRowIterator(rs);
        } catch (SQLException ex) {
            return null;
        }
    }
}</t>

classe DBRowIterator.java

/**
 *
 * @author Paulo Canedo C Rodrigues
 */
public class DBRowIterator<t> implements Iterator<map <String, T>> {
 
    private ResultSet rs;
    private Map<string , T> rowFields = new HashMap</string><string , T>() {
 
        //Sobrescrita do metodo get do mapa para transformar o get em case insensitive
        @Override
        public T get(Object key) {
            return super.get(((String) key).toUpperCase());
        }
    };
    private String[] columnsName;
    private boolean objectReaded = false;
    private int columnCount;
 
    DBRowIterator(ResultSet rs) throws SQLException {
        this.rs = rs;
 
        columnCount = rs.getMetaData().getColumnCount();
        columnsName = new String[columnCount];
 
        for (int i = 0; i < columnCount; i++) {
            columnsName[i] = rs.getMetaData().getColumnName(i + 1).toUpperCase();
        }
    }
 
    public boolean hasNext() {
        if (objectReaded == false && rowFields.size() != 0) {
            return true;
        }
 
        try {
            boolean flag = rs.next();
            if (flag = true) {
                rowFields.clear();
                for (int i = 0; i < columnCount; i++) {
                    Object o = rs.getObject(i + 1);
                    rowFields.put(columnsName[i], (T) o);
                }
            } else {
                if (rowFields != null) {
                    try {
                        rs.getStatement().close();
                        rs.close();
                    } catch (Exception ex) {
                    }
                }
                rowFields = null;
            }
            return flag;
        } catch (Exception ex) {
        }
        return false;
    }
 
    public Map<String, T> next() {
        objectReaded = true;
        return rowFields;
    }
 
    public void remove() {
    }
}
</string></map></t>

Abaixo segue dois exemplos de utilização, por favor, deixem comentário sobre o que achou das classes e da ideia.

java.sql.Connection connection = null;
 
//Exemplo 1 com genérico sem casting - apenas para tipo de dados homogêneos
DBQuery dbQuery1 = new DBQuery(connection, "SELECT nome, sobrenome, endereco FROM tb_agenda ORDER BY nome");
for (Iterator<map <String, String>> it = dbQuery1.iterator(); it.hasNext();) {
    Map<string , String> linhaDaConsulta = it.next();
    String nome = linhaDaConsulta.get("nome"); //nome do campo da consulta: case insensitive
    String sobrenome = linhaDaConsulta.get("sobrenome");
    String endereco = linhaDaConsulta.get("endereco");
    System.out.println(String.format("Nome: %s; Sobrenome: %s; Endereço: %s", nome, sobrenome, endereco));
}
 
//Exemplo 2 sem genérico com casting
DBQuery dbQuery2 = new DBQuery(connection, "SELECT nome, idade, peso FROM tb_pessoa ORDER BY nome");
for (Iterator<map <String, Object>> it = dbQuery2.iterator(); it.hasNext();) {
    Map<string , Object> linhaDaConsulta = it.next();
    String nome = (String) linhaDaConsulta.get("nome");
    Integer idade = (Integer) linhaDaConsulta.get("idade");
    Float peso = (Float) linhaDaConsulta.get("peso");
    System.out.println(String.format("Nome: %s; Idade: %d; Peso: %.2f", nome, idade, peso));
}</string></map></string></map>