quinta-feira, 13 de maio de 2010

Visitor sem accept()

O padrão Visitor é deveras útil, mas pode ser melhorado com um pouco de reflexão. O maior problema dele é que é preciso adicionar um método para cada classe a ser visitada. Geralmente o código não faz mais do que chamar o respectivo método visit() no Visitor. Com reflexão, pode-se eliminar esses métodos. Eliminar código é sempre bom!

Vamos começar pelas classes que serão visitadas.

class Circle { 
  public String toString() { 
    return "Circle"; 
  } 
}

class Square { 
  public String toString() { 
    return "Square"; 
  } 
}

class FunnySquare extends Square { 
  public String toString() { 
    return "FunnySquare"; 
  } 
}

class ReallyFunnySquare extends FunnySquare { 
  public String toString() { 
    return "ReallyFunnySquare"; 
  } 
}

Duas são subclasses de Square e para elas não vou criar métodos visit(). O Visitor com reflexão está no código abaixo.

class Visitor {
  
  private void visit(Circle c) { 
    System.out.printf( "I visited a Circle (%s)\n", c); 
  }
  
  private void visit(Square s) { 
    System.out.printf("I visited a Square (%s)\n", s);
  }

  public void visit(Object e) throws Exception {
    visit(e, e.getClass());
  }
   
  private void visit(Object e, Class c) throws Exception {
    try {
      Method m = getClass().getDeclaredMethod("visit", c);
      m.invoke(this, e);
    } catch(NoSuchMethodException nsme) {
      visit(e, c.getSuperclass());
    } catch(Exception ex) {
      throw ex;
    }
  }
}

O único método público é o visit(Object e). Ele usa uma versão privada que procura um método adequado para a classe do alvo e, se não achar, recursivamente procura métodos usando a superclasse do alvo.

O código abaixo executa um pequeno teste.

Circle c = new Circle();
Square s = new Square();
FunnySquare f = new FunnySquare();
ReallyFunnySquare r = new ReallyFunnySquare();

Visitor v=new Visitor();
for(Object o : new Object[] {
        new Circle(), new Square(), new FunnySquare(), new ReallyFunnySquare()
  }) {
    v.visit(o);
  }

E o resultado é:

I visited a Circle (Circle)
I visited a Square (Square)
I visited a Square (FunnySquare)
I visited a Square (ReallyFunnySquare)

Eu deixei apenas um método visit() público para evitar que o cliente use um método mais abrangente. Como o compilador escolhe o método conforme o tipo da referência e não o tipo do objeto, se for utilizada uma referência com tipo de uma superclasse da instância, perde-se a oportunidade de usar um método mais específico.

Um bônus nisso tudo é que não é preciso usar ifs.

quarta-feira, 12 de maio de 2010

Império dos 8 bits

Diziam os ingleses que o sol nunca se punha no Império Britânico. Eu resolvi descobrir qual a extensão do Império dos 8 bits. Isto é, quais países produziram micros de 8 bits durante a década de 1980.

Comecei pelas Américas e descobri os seguintes países:
  1. Argentina;
  2. Brasil;
  3. Canadá e
  4. Estados Unidos.
Os Estados Unidos, como era de se esperar, desenhou e fabricou a maior parte dos micros. Canadá parece ter tido apenas uma empresa (Semi-Tech) que desenhou um micro (Pied Piper) baseado no Z80 e o produziu nos Estêites. A Argentina tinha 2 fabricantes. A Czerweny fabricava clones do ZX Spectrum (devidamente licenciados) e a Talent/Telematica fabricou 3 modelos de MSX. O Brasil teve um pouco mais de variedade, provavelmente por causa da reserva de mercado.

E o império começa a tomar forma.

domingo, 9 de maio de 2010

Dia da mãe tecnológica

Não tenho paciência com quem tem medo de computador. Geralmente são as mesmas pessoas que ignoram o limite de velocidade na estrada ou tomam o remédio que a vizinha indicou.

Minha mãe começou a usar computador para editar textos no WordWise do BBC B+ com 128KB de RAM. Com ele, edita-se o texto em um modo texto de 40 colunas por 25 linhas. Para saber mais ou menos como vai sair a impressão, usa-se um modo de 80 colunas de 2 cores. Não chegava a ser uma limitação, porque a impressora era matricial (Epson LX80) e monocromática. Havia memória livre para 28KB de texto. Então, para um trabalho grande, era preciso dividir o texto em capítulos. O texto era decorado com palavras-chave que indicavam a formatação. Era quase como programar. Nos disquetes de 5,25" cabiam 400KB.

Depois veio o WordStar. Ainda não era WYSIWYG, mas pelo menos a edição era feita em 80 colunas. Os comandos eram digitados com seqüências estranhas e a mãe os dominava como ninguém: Ctrl-K-B, Ctrl-K-K, etc. A impressora ainda era matricial e os disquetes só continham 360KB.

Finalmente, chegou o Word, começando no Windows 3.11. Desde então, as coisas não mudaram muito, inclusive a resistência de muitos.

Sempre que escuto "estou muito velho para isso" ou "isso é muito difícil", tenho que conter o ímpeto de retrucar com "se até a minha mãe consegue", porque a verdade é que ela tem uma habilidade extraordinária de aprender e não tem medo de enfrentar tecnologia nova.

Feliz dia das mães!

sábado, 8 de maio de 2010

30h de discussão

O controverso (e pouco amado) Sindppd-RS quer reduzir a jornada de trabalho do setor de TI para 30h. É uma discussão que levanta opiniões bem fortes e, em grande parte, pouco elaboradas.

Não me parece absurdo, porque, tendo já trabalhado 30h, sei que é possível fazer bastante com 6h diárias ininterruptas. Mas parece que há um sentimento de que há mais valor em trabalhar muito, como se mais horas criassem exponencialmente mais valor. Pessoalmente, acho que a semana de 40h está de bom tamanho, mas as 40h do hemisfério norte (das 9h às 5h). Lá as pessoas precisam levar os filhos ao colégio, pagar as contas e fazer compras no fim do dia. Aqui, parece que não.

De qualquer forma, não acho que exista um número mágico que seja bom para todos. Por isso, creio que seria mais sensato se as empresas oferecessem vagas com cargas horárias alternativas. Um estudante poderia trabalhar 20h e um pai poderia oferecer 30h. Algumas já fazem isso.

As empresas de TI não costumam vender exatamente o mesmo produto para todos os seus clientes. Não vejo por que tenham que oferecer exatamente o mesmo contrato de trabalho a todos os seus funcionários.

quinta-feira, 6 de maio de 2010

Mais furos no SQL

A primeira solução para encontrar espaços na numeração de uma chave primária tinha um defeito: se não existisse registro para o primeiro número, todos os números até o registro mais baixo permaneceriam vagos.

Pode-se resolver a questão mudando um pouco a consulta. No lugar do olhar para frente, com LEAD(), olha-se para trás, com LAG().


select numero2+livre
from
(select numero numero1,
lag(numero, 1, 0) over (order by numero) numero2
from produtos) p1
inner join (select rownum livre from produtos) p2
on livre < numero1 - numero2
order by 1


Usei lag(numero, 1, 0), porque quero que a numeração comece com 1 (o segundo select usa rownum e este começa com 1).

quarta-feira, 5 de maio de 2010

Furos no SQL

Um dos problemas com os quais mais quebrei a cabeça foi o de encontrar números vagos numa chave primária. A tabela usava como chave apenas um número e para que ele não crescesse indefinidamente (e porque era comum entrarem e saírem registros) eu precisava achar posições vagas para os registros novos.

A solução funcionava, mas era um emaranhado de consultas aninhadas e espertezas numéricas.

Pois bem, com as funções analíticas as coisas ficaram muito mais fáceis!

Suponha, para a consulta abaixo, que eu tenha uma tabela de números de produtos e que quero reaproveitar um número quando um produto desaparecer. Usando a função LEAD(), posso saber, para cada número, qual o próximo número na tabela. Fazendo um join com a própria tabela e fazendo um joguinho rápido com rownum, posso enumerar todos os valores livres, mesmo que os números sejam maiores que o número de registros na tabela. É o caso de haver 100 registros com números do tipo 8 posições; a consulta só vai falhar se o maior furo for maior que o número de registros. Nesse caso, basta achar uma tabela maior para alimentar o rownum (e supondo que seja mesmo necessário achar todos os valores vagos de uma só vez).


select numero1+livre
from
(select numero numero1,
lead(numero, 1, null) over (order by numero) numero2
from produtos) p1
inner join (select rownum livre from produtos) p2
on livre < numero2 - numero1
order by 1


A coluna numero2 tem o próximo valor ocupado e a diferença entre numero2 e numero1 é o número de lugares vagos mais um. No select externo, numero1+livre produz os valores disponíveis.

domingo, 2 de maio de 2010

Pérolas dos usuários III

Arthur C. Clarke escreveu em um de seus livros que "não se pode distinguir de magia qualquer tecnologia suficientemente avançada". Esse momento eu testemunhei muito antes de 3001.

Um belo dia de sol, uma britadeira começou a trabalhar na rua abaixo do escritório. O sistema olhou nos olhos perplexos do usuário. Por que o computador está fazendo esse barulho? Queimará?

A máquina desligou e o barulho continuou. Como pode o sistema fazer barulho com o computador desligado? Mesmo tirando da tomada! Não pode ser! Os elétrons não estão a fluir!

Mas fez.