quarta-feira, 16 de dezembro de 2009

Novo endereço

Não mais atualizarei o blogger, a partir de hoje vou postar em http://besen.posterous.com.

So long, and thanks for all the fish :)

Testes parametrizados com JUnit

Postei no blog da OnCast um texto sobre testes parametrizados com JUnit.

Enjoy!

domingo, 30 de agosto de 2009

Software Engineering: An Idea Whose Time Has Come and Gone?

Li um artigo bacana do Tom DeMarco (bastante conhecido pelo livro Peopleware, que é bem interessante por sinal) sobre a mudança de visão dele do gerenciamento de projetos de software.

A visão dele é consistente com o que vem se falando ultimamente, que ao invés de fazer analogias com a construção civíl, desenvolvimento de software é mais parecido com "escrever", ou outras formas de produção artística. Não existe um jeito muito prático de mensurar como um autor de livro está desempenhando seu papel. (Pelo menos não antes do livro ser lançado, hehe)

Achei bacana a atitude do Tom DeMarco de reconhecer que o trabalho dele já não é mais válido, e com certeza é uma prova de que metodologias ágeis tem algum mérito.

domingo, 16 de agosto de 2009

Guia do Mochileiro das Galáxias

Sem nenhum motivo especial, vou colocar aqui os meus quotes preferidos do primeiro livro dessa fantástica trilogia de quatro (+1) livros.


  • Enquanto isso, o pobre peixe-babel, por derrubar os obstáculos à comunicação entre os povos e culturas, foi o maior responsável por guerras sangrentas, em toda a história da criação.

  • Quando um [não mochileiro] descobre que um mochileiro tem uma toalha, ele automaticamente conclui que ele tem também escova de dentes, esponja, sabonete, lata de biscoitos, garrafinha de aguardente, bússola, mapa, barbante repelente, capa de chuva, traje espacial, etc, etc.

  • (…) a geografia de Damogran não é nada prática. Consiste apenas em algumas ilhas desertas de tamanho médio e grande, separadas por oceanos de rara beleza, mas de uma vastidão chatíssima.

  • Prostetnic Vogon Jeltz sorria muito devagar – não que ele quisesse fazer gênero, estava tentando lembrar-se da sequência de contrações musculares necessárias para realizar o ato.

  • Trillian desconfiava que ele [Zaphod] conseguia levar uma vida tão louca e bem-sucedida principalmente por não entender jamais o verdadeiro significado de nada que ele fazia.

  • Fazia tudo com uma mistura de extraordinária genialidade e incompetência ingênua, sendo muitas vezes difícil saber distinguir uma coisa da outra.

  • Ah, a vida – disse Marvin, lúgebre. - Pode-se odiá-la ou ignorá-la, mas é impossível gostar dela.
Pra quem não leu, recomendo fortemente toda a série.

sexta-feira, 26 de junho de 2009

Executors/FutureTask Vs. Oberver

Antes de começar, este post é específico pra Java. Se não se interessar por java, pule pro próximo post, ou não.

Pra quem não conhece, o padrão observer serve basicamente pra monitorar mudanças de estado em um objeto. Como todo padrão de projeto, o observer pode ser subvertido para fazer coisas onde existem opções melhores.

Exemplo: é preciso mostrar um diálogo com "OK" e "Cancel" para o usuário, e uma ação externa deve ser executada, dependendo da resposta do usuário.

Uma primeira idéia é cadastrar um listener no diálogo, e dependendo do botão clicado, um método do listener é notificado. Um exemplo de listener seria:

/**
 * Interface de um Listener.
 */
public interface Listener {

 /**
  * Método notificado ao cancelar o diálogo.
  */
 void onFailure();

 /**
  * Método notificado ao aceitar o diálogo.
  */
 void onSuccess();

}

Uma possivel implementação de diálog usando esse listener:

/**
 * Implementação de um diálogo onde a resposta da ação do usuário é capturada
 * através de um listener.
 */
class ListenerFrame {
 /** Frame principal. */
 private final JFrame frame;

 /** Ação do botão de ok. */
 private final ActionListener okAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   listener.onSuccess();
  }
 };

 /** Ação do botão de cancelar. */
 private final ActionListener cancelAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   listener.onFailure();
  }
 };

 /** Instância do listener que deve ser noticada. */
 private final Listener listener;

 /**
  * Constroi uma instância de ListenerFrame.
  * 
  * @param l
  *            o listener a ser notificado.
  */
 public ListenerFrame(final Listener l) {
  listener = l;
  frame = new JFrame();
  final JButton ok = new JButton("OK");
  ok.addActionListener(okAction);
  final JButton cancel = new JButton("Cancel");
  cancel.addActionListener(cancelAction);
  final JPanel panel = new JPanel();
  panel.add(ok);
  panel.add(cancel);
  frame.add(panel);
  frame.pack();
 }

 /**
  * Exibe o frame.
  */
 public void show() {
  frame.setVisible(true);
 }
}

Como podem ver, no botão ok "onSuccess()" é chamado, e em cancelar "onFailure()" é chamado.

Uma classe usando esse diálogo, a título de exemplo:

/**
 * Exemplo de utilização de um frame onde a resposta é dada por um listener.
 */
public class ListenerFrameExample {

 /**
  * O programa cria um novo ListenerFrame passando um listener como
  * argumento.
  * 
  * @param args
  */
 public static void main(final String[] args) {
  new ListenerFrame(new Listener() {
   @Override
   public void onSuccess() {
    System.out.println("YAY!");
    System.exit(0);
   }

   @Override
   public void onFailure() {
    System.out.println("OH NOES!");
    System.exit(0);
   }
  }).show();
 }
}

O problema aqui é que eu estou usando o padrão observer pra esperar uma resposta, e não uma mudança de estado. Se eu puder continuar com a execução do programa, isso até não tem tanto problema, mas a intenção aqui claramente não é essa. E como intensão em um código é (quase) tudo, deve haver um jeito melhor de fazer isso ;)

É ai que entra a FutureTask. Como podem na documentação, a FutureTask serve pra guardar um resultado que vai ser requisitado no futuro, e é exatamente isso que queremos: saber o futuro resultado da ação do usuário.

Primeiro, podemos esquecer a classe Listener. Toda a parde de resposta vai ser encapsulada no diálogo.

/**
 * Implementação de um diálogo onde a resposta da ação do usuário é capturada
 * por uma FutureTask.
 * 
 */
class ExecutorFrame {
 /** Frame principal. */
 private final JFrame frame;

 /** Variável auxiliar utilizada para capturar a resposta do usuário. */
 private Boolean success;

 /** FutureTask criada para encapsular a resposta do usuário. */
 private final FutureTask hadSuccess = new FutureTask(
   new Callable() {
    public Boolean call() throws Exception {
     return success;
    };
   });

 /** Ação do botão de ok. */
 ActionListener okAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   success = true;
   // FutureTask é executada, liberando hadSuccess()
   Executors.newSingleThreadExecutor().execute(hadSuccess);
  }
 };

 /** Ação do botão de cancelar. */
 ActionListener cancelAction = new ActionListener() {
  @Override
  public void actionPerformed(final ActionEvent e) {
   success = false;
   // FutureTask é executada, liberando hadSuccess()
   Executors.newSingleThreadExecutor().execute(hadSuccess);
  }
 };

 /**
  * Constroi uma instância de ExecutorFrame.
  */
 public ExecutorFrame() {
  frame = new JFrame();
  final JButton ok = new JButton("OK");
  ok.addActionListener(okAction);
  final JButton cancel = new JButton("Cancel");
  cancel.addActionListener(cancelAction);
  final JPanel panel = new JPanel();
  panel.add(ok);
  panel.add(cancel);
  frame.add(panel);
  frame.pack();
 }

 /**
  * Exibe o frame.
  */
 public void show() {
  frame.setVisible(true);
 }

 /**
  * Retorna a resposta do usuário. O método fica travado até que o usuário
  * clique em um botão.
  * 
  * @return {@code true} caso o usuario clique em OK, {@code false} caso
  *         clique em cancelar.
  */
 public boolean hadSuccess() {
  try {
   return hadSuccess.get();
  } catch (final InterruptedException e) {
  } catch (final ExecutionException e) {
  }
  return false;
 }
}

O método "get()" da FutureTask trava a execução até a tarefa seja executada. Uma FutureTask é criada para guardar o resultado, e as ações dos botões simplesmente executam a tarefa, fazendo assim com que "hadSuccess()" retorne o resultado correto.

A utlização desse dialogo fica assim:

/**
 * Exemplo de utilização de um frame onde a resposta é dada por uma FutureTask.
 */
public class ExecutorFrameExample {

 /**
  * O programa instancia um frame e aguarda a sua resposta. Note que
  * d.hadSuccess() fica travado até o usuário clicar em um botão.
  * 
  * @param args
  */
 public static void main(final String[] args) {
  final ExecutorFrame d = new ExecutorFrame();
  d.show();
  if (d.hadSuccess()) {
   System.out.println("YAY!");
  } else {
   System.out.println("OH NOES!");
  }
  System.exit(0);
 }
}

Eu particularmente acho bem mais interessante desse jeito. Obviamente cada implementação tem suas vantagens e desvantagens, mas a intenção da segunda opção está bem mais clara do que a primeira.

Outras leituras:
http://en.wikipedia.org/wiki/Swing_%28Java%29
http://en.wikipedia.org/wiki/Observer_pattern
http://sourcemaking.com/design_patterns

terça-feira, 14 de abril de 2009

Python: PyQt Model/View

Imagino que todos conheçam o padrão de projeto MVC, o famoso Model/View/Controller. A idéia é separar a visualização (interface com o usuário) do controle (os "comandos" que o usuário executa) e do modelo (os dados e a lógica de negócio).

Uma pequena variação disso é o model/view, quando não é necessário controle, ou o controle está em um outro lugar. O MVC completo é um padrão arquitetural, sendo que o "MV" é um padrão mais localizado. Acredito que isso fique mais claro ao final do post :-).

A partir do Qt 4, alguns widgets passaram a suportar o padrão MV. Os antigos QListWidget, QTableWidget, etc. foram substituídos pelo QListView, QTableView, etc. As classes de modelo foram adicionadas para completar o padrão, e para conectar ambos basta um simples someView.setModel(someModel).

Um exemplo usando algumas classes padrão:

import sys

from PyQt4.QtCore import *
from PyQt4.QtGui import *

app = QApplication([])

view = QListView()
model = QStringListModel("item1 item2 item3 item4 item5".split())

view.setModel(model)

view.show()

sys.exit(app.exec_())

Qualquer mudança no model é refletida automaticamente na view, e é isso que torna o padrão interessante. É possível ter várias views para um model, e também existe o conceito dos delegates, que servem para dar flexibilidade à entrada de dados. Para não me alongar muito, não vou entrar nos detalhes, mas isso tudo pode ser visto com mais profundidade em Qt Model/View Programming.

model.setData(model.index(0), QVariant("aaaa"))



Agora um exemplo de classe que implementa um modelo abstrato. Supondo que eu queira um modelo de uma série de resultados numéricos, como proceder?

O primeiro passo é criar uma classe que implemente uma QAbstractTableModel:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class ResultTableModel(QAbstractTableModel):
    def __init__(self, parent = None):
        super(QAbstractTableModel, self).__init__(parent)
        self.results = []
    

Como pode ser visto aqui, o meu modelo deve sobrescrever alguns métodos para que ele possa utilizado. Caso o modelo necessite apenas ser lido (read-only), os métodos flags(), data(), headerData() e rowCount() devem ser providos. Este quatro métodos são suficientes para um QAbstractListModel, mas como estou implementando uma tabela, columnCount() também deve ser provido.

Uma implementação básica seria:

def rowCount(self, parent = QModelIndex()):
    return len(self.results)

def columnCount(self, parent = QModelIndex()):
    return 1 # por enquanto não quero mais de uma coluna

def flags(self, index):
    # quero todos os itens habilitados e editáveis
    return Qt.ItemIsEnabled | Qt.ItemIsEditable

# Recupera um dado de um índice (QModelIndex)
def data(self, index, role):
    # índice não é válido, então retorno um dado vazio
    if not index.isValid():
        return QVariant()

    # se a linha está fora dos bounds da minha lista, resultado vazio
    if index.row() >= len(self.results):
        return QVariant()

    # se o role é o DisplayRole (ou seja, o valor que deve ser renderizado)
    # retorno o valor que esta na linha lista
    if role == Qt.DisplayRole:
        return QVariant(self.results[index.row()])
    else:
        # podemos retornar valores específicos para outros roles,
        # por exemplo ToolTipRole. Pela simplicidade, ignoramos isso.
        return QVariant()

# Recupera os cabeçalhos da tabela
def headerData(self, section, orientation, role):
    if role != Qt.DisplayRole:
        return QVariant()

    if orientation == Qt.Horizontal:
        # Results vai aparecer no topo da linha
        return QVariant("Results")
    else:
        # em cada linha, mostrar o numero dela no início
        return QVariant("%s it." % (section + 1,))

Pronto, agora já podemos exibir o modelo em uma QTableView :-).

No próximo post irei mostrar como fazer para um model ser editável e redimensionável.

Outros materiais:
MVC
Model/View Programming
Model Subclassing
QTableView
QAbstractTableModel
Qt Documentation

quinta-feira, 26 de março de 2009

Python: (des)empacotando argumentos

Packing/unpacking de argumentos é algo que as vezes é esquecido em Pytho e é bem útil em algumas situações.

Packing permite uma função receber um número arbitrário de argumentos:

In [1]: def a(*b):
   ...:     print len(b), b
   ...:
   ...:

In [2]: a(1,2,3,4,5)
5 (1, 2, 3, 4, 5)

O unpacking faz o serviço contrário, ou seja, ao chamar uma função, os argumentos de uma tupla podem ser "explodidos" (na falta de uma palavra melhor). Aqui vai um exemplo bem prático disso, usando o QColor do PyQt:

color = (255, 0, 0)
QColor.fromRgb(*color) # ao invés de QColor.fromRgb(color[0], color[1], color[2])

A função recebe 3 argumentos (r, g, b), e usando o unpacking economizamos alguns caracteres.

Tem mais algumas dicas na documentação oficial, incluindo a possibilidade de usar isso pra keyword arguments usando a notação "**".

quinta-feira, 19 de março de 2009

Internet Explorer 8 Final!

Firefox

'nuf said.

domingo, 1 de março de 2009

ZFS

O FLOSS Weekly entrevistou nessa semana Aaron Newcomb e David Brittle, falando sobre o ZFS. Pra quem não sabe, ZFS é um sistema de arquivos que vem sendo desenvolvido pela Sun há um tempo, com características bem interessantes.

A entrevista tem cerca de 75 minutos, e pode ser encontrada aqui.

quinta-feira, 15 de janeiro de 2009

O Arqueiro

Comecei a ler O Arqueiro, de Bernard Cornwell. Vou colocar dois quotes do livro aqui, pra passar minha impressão do livro.

- Filho da puta idiota - disse Jake. - Eu não ia machucá-lo. Não muito.
- Eu vou me embebedar, padre - disse Thomas, feliz. - Vou ficar tão bêbado que uma daquelas duas garotas vai parecer atraente. - Ele fez um gesto com a cabeça em direção às filhas da viúva.
O padre Hobbe inspecionou-as com ar crítico, e depois suspirou.
- Você vai se matar bebendo tanto assim, Thomas.
Ou seja, excelente!

analytics