Mostrando postagens com marcador Java. Mostrar todas as postagens
Mostrando postagens com marcador Java. Mostrar todas as postagens

segunda-feira, 16 de agosto de 2010

Diversão com ANTLR

ANTLR – Another Tool for Language Recognition – é uma ferramenta para construção de analisadores léxico (lexer) e sintático (parser) de linguagens formais para JVM.

A grosso modo, é uma ferramenta para implementação de linguagens de programação.

E ANTLRWorks é um IDE gráfico para ANTLR.

Para entender como funciona ANTLR, vou explicar o exemplo do próprio sítio. Então instale o ANTLRWorks e mãos à obra!

Avaliador de expressões


Quando iniciar o ANTLRWorks, crie uma gramática chamada Expr.g.

O código já vai aparecer com a seguinte linha:
grammar Expr;


A ideia aqui é criar um interpretador que avalie expressões matemáticas simples, inclusive com variáveis.

Para suportar variáveis, precisamos de algum lugar para armazenar seus valores. Para isso usamos Java:
@header {
import java.util.Map;
import java.util.HashMap;
}

@members {
Map<String, Integer> memory = new HashMap<String, Integer>();
}


Isso criará o contentor memory, que é um mapa de chaves string e valores inteiros.

ANTLR funciona definindo regras. Nossa primeira regra é programa (prog) e precisamos defini-la:
prog:   stat+;


Então nosso programa é um grupo (+) de comandos (stat), o que nos leva à próxima regra.

Definimos comando como:
stat:   expr NEWLINE { System.out.println($expr.value); }
| ID '=' expr NEWLINE
{ memory.put($ID.text, new Integer($expr.value)); }
| NEWLINE
;


Calma, vou explicar!

Um comando pode ser uma expressão (expr) seguida de uma mudança de linha (NEWLINE). Neste caso, será impresso na saída padrão (System.out) o valor da expressão ($expr.value);

Um comando também pode ser (| significa OU) um identificador (ID) seguido de um sinal de igual ('='), uma expressão e uma mudança de linha. Neste caso o valor da expressão ($expr.value) será armazenado no contentor memory na chave igual ao texto do identificador ($ID.text).

Um comando ainda pode ser uma mudança de linha, que não fará nada.

Agora precisamos definir uma série de regras: expr, NEWLINE e ID. Vamos começar por expr:
expr returns [int value]
: e=multExpr { $value = $e.value; }
( '+' e=multExpr { $value += $e.value; }
| '-' e=multExpr { $value -= $e.value; }
)*
;


Quer dizer que expr retorna um valor inteiro (int value) e consiste de uma expressão múltipla (multExpr) seguido de nenhuma, uma ou mais repetições (*) do que estiver entre parêntesis. O valor retornado é igual ao valor da expressão múltipla.

A atribuição e= é necessária por haver mais de uma ocorrência de multExpr.

Entre parêntesis tempos o sinal de adição ('+') seguido de uma multExpr ou (|) um sinal de subtração ('-') seguido de uma multExpr. No primeiro caso o valor da multExpr é adicionado ao valor retornado, no segundo é subtraído.

A regra multExpr é definida como:
multExpr returns [int value]
: e=atom { $value = $e.value; } ('*' e=atom { $value *= $e.value; })*
;


Uma multExpr retorna um valor inteiro e consiste de um ou mais atómicos (atom). Os valores dos atómicos são multiplicados.

Definindo um atómico:
atom returns [int value]
: INT { $value = Integer.parseInt($INT.text); }
| ID
{
Integer v = memory.get($ID.text);
if (v != null) $value = v.intValue();
else System.err.println("undefined variable " + $ID.text);
}
| '(' expr ')' { $value = $expr.value; }
;


Então um atómico pode ser um inteiro (INT, o valor é o corpo do inteiro: $INT.text), um identificador (ID, o valor é o relacionado à chave no contentor memory – se não existir, gera um erro) ou o resultado de uma expressão (expr), que deve estar entre parêntesis.

Falta definir as regras ID, INT e NEWLINE:
ID  :   ('a'..'z'|'A'..'Z')+;
INT : '0'..'9'+;
NEWLINE: '\r'? '\n';


Ou seja: identificador é uma sequência de letras, inteiro uma sequência de números e mudança de linha é CRLF ou LF.

Para finalizar, para ignorar espaços em branco:
WS  :   (' '|'\t')+ { skip(); };


Testando


Para testar, clique em Debug e preencha a caixa Text com:
x = 1
y = 2
3 * (x + y)


Os parêntesis e a mudança de linha na última linha são importantíssimos!

Clique Ok e divirta-se acompanhando o debugger! Repare que, ao final da execução, vai mostrar 9 na aba Output.

[]’s
Cacilhας, La Batalema

terça-feira, 6 de outubro de 2009

IDEs em ambiente GNU/Linux

Poliedro Semana passada escrevi um artigo sobre alguns ambientes de desenvolvimento integrado e editores de texto, alguns deles RAD. No entanto o artigo ficou muito longo, daí tive a ideia de quebrá-lo em três partes.

Publiquei em meu blog Reflexões de Monte Gasppa e Giulia C., mas agora, pensando bem, acho que cabe pelo menos uma referência aqui no Kodumaro.

As três partes do artigo são:
  1. Editores de texto
  2. IDEs focadas em projeto
  3. RAD


A quem se interessar, boa leitura!

[]'s
Cacilhas, La Batalema

sexta-feira, 21 de agosto de 2009

Dev in Rio 2009

Gente,

Notícia fresquinha no blog do Henrique Bastos:

Dev in Rio 2009: EU VOU!


Finalmente o Rio de Janeiro ganhou um grande evento de tecnologia focado no que é mais importante: Pessoas!

É com muito orgulho que apresentamos o Dev in Rio 2009, uma conferência bombástica sobre desenvolvimento de software que acontecerá no próximo dia 14 de setembro no Centro de Convenções SulAmérica, no Rio de Janeiro!

O Dev in Rio 2009, contará com palestrantes nacionais e internacionais que falarão sobre Java, Ruby e Ruby on Rails, Python, Django, Open Source, Joomla!, Métodos Ágeis, e muito mais. Nomes como Fábio Akita, Vinícius Manhães Teles, Jacob Kaplan-Moss, Guilherme Silveira, Nico Steppat, Ryan Ozimek e Jeff Patton agitarão um dia inteiro de muita tecnologia e diversão. Veja a programação detalhada no site da Dev in Rio 2009.

Mas não é só isso. Como eu disse no início, este é um evento focado em pessoas. Toda a organização do evento está voltada para promover ao máximo a interação e integração entre os presentes.

Por isso, o Dev in Rio 2009 contará com uma Arena DojoRio onde participantes e palestrantes poderão programar lado à lado, experimentando diversas linguagens e técnicas, buscando juntos as melhores formas de desenvolver software.

O evento está sendo organizado por mim (Henrique Bastos) em parceria com o meu amigo Guilherme Chapiewski, contando com todo o apoio dos membros da PythOnRio, DojoRio e #Horaextra.

A realização está sendo coordenada pelas nossas experientes amigas da Arteccom. E tê-las ao nosso lado já garante que este será um evento para marcar o circuito carioca.

Você não pode perder esta incrível oportunidade de participar desse mega evento, interagir com grandes nomes do cenário mundial de tecnologia, conhecer pessoas interessantes que compartilham a paixão pelo desenvolvimento de software, e ainda por cima começar sua semana só na terça-feira. Inscreva-se já!

Nos vemos por lá!

[]’s!


[]'s
Cacilhas, La Batalema

quinta-feira, 19 de março de 2009

Sobre APIs

Umas das coisas mais importantes em programação, sem a qual ninguém consegue desevolver, é API.

API – Interface de Programação de Aplicativos – é o conjunto de recursos de que o programador dispõe para desenvolver suas aplicações.

Este artigo faz uma comparação – na verdade, mais uma demonstração – de cinco APIs muito usadas: IEEE 1003, STL, OpenStep, GLib e Java.

Para a demonstração será usado um trecho de código para ler a primeira linha de um arquivo.

Por que não a API do Windows?


Antes de começar, é preciso esclarecer por que não a API do Windows.

O objetivo de uma API é oferecer um ambiente homogêneo de programação, para que o programador não seja obrigado a reaprender a mesma coisa diversas vezes. Para facilitar isso, muitas APIs ainda podem ser usadas em diversas linguagens diferentes, outras em apenas uma.

No entanto uma API que funciona apenas em um único sistema operacional – ou ainda no grupo de sistemas operacionais de uma única empresa – não está fazendo seu trabalho, por mais usado que esse(s) sistema(s) possa ser.

Assim a API do Windows não faz mais do que se espera da interface de um sistema operacional.

É claro que é necessário que o Windows possua uma API, mesmo que seja exclusivamente para conexão de outras APIs mais universais.

IEEE 1003 (POSIX)


O padrão POSIX, definido pela IEEE, oferece uma interface completa para sistemas compatíveis com UNIX. Alguns exemplos são IBM AIX, Solaris, GNU (e daí também Linux), BSD, Mac OS X, MS Xenix e muitos outros.

O trecho de código em C para ler a primeira linha de um arquivo:
FILE *fd = fopen(filename, "r");

if (fd) {
while (!eof(fd)) {
char buf;
fread(&buf, sizeof(char), 1, fd);
printf("%c", buf);
if (buf == '\n')
break;
}

fclose(fd);
}


Observação1: é preciso incluir o cabeçalho stdio.h ou, em C++, cstdio:
#include <stdio.h>


Observação2: a variável filename é do tipo const char *.

Observação3: foi preciso procurar ocorrência do carácter \n pois POSIX não ofereça uma função de alto nível para ler uma linha.

Observação4: o padrão POSIX é completamente procedimental – as demais APIs citadas aqui são orientadas a objetos –, sendo um pouco desajeitado para algumas operações.

STL


A linguagem de programação C++ possui uma biblioteca padrão própria bastante completa chamada STL – Standard Library.

Para o exemplo, é preciso incluir o cabeçalho fstream para a manipulação de arquivo e o cabeçalho iostream para a exibição dos dados na tela:
std::ifstream fd(filename);

if (fd.is_open()) {
char buf[1024];
fd.getline(buf, 1023);

std::cout << buf << std::endl;
fd.close();
}


Observação1: como citado acima, é preciso incluir pelo menos o cabeçalho fstream ao manipular arquivos:
#include <fstream>


Observação2: a variável filename precisa ser do tipo const char *std::ifstream não suporta std::string.

OpenStep


A empresa NeXT de Steve Jobs, ora adquirida pela Apple, abriu a API de seu sistema operacional em 1992 num acordo com a SUN Microsystems, sob o nome de OpenStep, o que permitiu, além da criação de aplicações de terceiros para NeXTSTEP, também o desenvolvimento de outros sistemas operacionais compatíveis com sua API e APIs de programação para sistemas já existentes, tornando-os compatíveis com OpenStep.

Exemplos de sistemas compatíveis com essa API são Mac OS X e GNUstep (portável para Windows e qualquer plataforma compatível com POSIX).

A API OpenStep funciona exclusivamente em Objective-C, apesar de haver alguns portes para outras linguagens, como C++, Smalltalk e Guile Scheme.

Vamos ao exemplo em Objective-C:
id fd = [NSFileHandle fileHandleForReadingAtPath: filename];

if (fd != nil) {
char buf = 0;
[fd seekToEndOfFile];
unsigned long long eof = [fd offsetFile];
[fd seekToFileOffset: 0];

while (([fd offsetInFile] < eof) && (buf != '\n')) {
[[fd readDataOfLength: 1] getBytes: &buf length: 1];
printf("%c", buf);
}

[fd closeFile];
}


Observação1: é preciso importar o cabeçalho Foundation.h:
#import <Foundation/Foundation.h>


Observação2: a variável filename precisa ser do tipo NSString.

Observação3: eu não conheço um método para ler uma linha de um arquivo em OpenStep, portanto usei um esquema similar ao usado acima em POSIX.

GLib


A GLib começou como parte do toolkit gráfico Gtk+ na versão 2.0, tendo evoluído para se tornar uma API multiplataforma orientada a objetos completa e independente da interface gráfica.

Você pode usar GLib em qualquer sistema POSIX e em Windows.

A maior curiosidade da GLib é que ela deixa explícita o equívoco – ou a falácia – de quem crê que não seja possível programar orientado a objetos em uma linguagem não orientada a objetos: é escrita em C e completamente orientada a objetos.

Aliás a base da GLib é o GObject que, curiosamente, não é compatível com a estrutura de orientação a objetos de C++ e outras linguagens clássicas, apesar de haver portes para C++ e outras.

O exemplo a seguir é um trecho de código em Vala, linguagem criada expecificamente para a GLib:
var file = File.new_for_path(filename);

if (file.query_exists(null)) {
try {
var fd = new DataInputStream(file.read(null));
stdout.printf("%s\n", fd.read_line(null, null));
fd.close(null);
} catch (Error e) {
// Ignora exceções
}
}


[update 2009-03-19]Faltou o close.[/update]

Observação1: é preciso «usar» GLib:
using GLib;


Observação2: a variável filename é do tipo string – equivalente em C a GString e gchar *.

Observação3: é preciso usar o pacote (--pkg) gio-2.0, que importa GIO, recursos de I/O da GLib.

Java


Em sua linguagem de programação, a SUN Microsystems optou por criar uma API própria (bem complicada e verborrágica em relação às demais, diga-se).
BufferedReader fd = null;
try {
fd = new BufferedReader(new FileReader(filename));
System.out.println(fd.readLine());

} catch (Exception e) {
// Ignora exceções

} finally {
try {
if (fd != null)
fd.close();
} catch (IOException e) {
// Ignora exceções
}
}


Observação1: as classes usadas estão no pacote java.io:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;


Observação2: a variável filename é do tipo String.


**

Fica aqui a dica de três APIs interessantes.

[]'s
Cacilhas, La Batalema

quarta-feira, 11 de março de 2009

Quem vai pra Vala?

live.gnome.org Dessa vez o pessoal do Gnome acertou a mão. Estou falando da linguagem de programação Vala.

Até hoje, quem queria programar em C# era obrigado a usar ou um sistema operacional problemático, ou uma ferramenta monstruosamente pesada e ineficiente.

Não mais.

O projeto Gnome lançou há algum tempo a linguagem Vala, fortemente baseada na sintaxe do C#, mas voltada para aplicações Gnome. Quem manja de C# ou maja de Java e já viu C# não terá dificuldade em lidar com Vala.

Instalação


Segundo a página do projeto, Vala foi feito para plataforma Gnome. Surgiu aí meu primeiro problema: uso Slackware e o Volkerding decidiu que Slackware não terá mais suporte a Gnome! Em sua total substituição, há o Xfce.

Simplesmente não há um pacote oficial de Gnome para Slackware – e os não-oficiais que já experimentei simplesmente detonaram o sistema todo.

Ainda mais: e quem usa outros SOs? Se não tiver Gnome já era?

Qual não foi minha agradável surpresa quando descobri que Vala compilou e instalou sem problemas no Slackware 12.2?! Sua dependência verdadeira é GTK+ 2.x, que você encontra em praticamente qualquer distribuição GNU/Linux ou BSD.

E Windows? Também tem!

Usando


Um «Olá Mundo» em Vala é bastante simples (tanto quanto Java, pelo menos):
using GLib;

public class HelloWorld : Object {
public static int main(string[] args) {
stdout.printf("Olá Mundo!\n");
return 0;
}
}


Ao contrário do .NET, não há código escondido do programador. Por exemplo:
using GLib;

public class HelloWorld : Object {

private Gtk.Window window;


public HelloWorld() {
window = new Gtk.Window(Gtk.WindowType.TOPLEVEL);
window.set_default_size(300, 200);
window.destroy += Gtk.main_quit;
window.add(new Gtk.Label("Olá Mundo!"));
}


public void show() {
window.show_all();
Gtk.main();
}


public static int main (string[] args) {
Gtk.init(ref args);
var sample = new HelloWorld();
sample.show();

return 0;
}
}


Antes que alguém reclame, peço desculpas pelos códigos mal orientados a objetos, mas não quis ter muito o que explicar.
[update]Ajeitei pra ninguém ficar me chateando.[/update]

Maturidade


Estranhamente a linguagem já está bastante madura na 0.5.7. Não encontrei quebras inusitadas nem erros bizarros.

[update 2009-04-01]
Encontrei uma quebra inusitada: Vala não lida bem com closures e escopo dinâmico. Ela se perde um pouco gerando alguns erros inesperados.

Outro erro inconveniente está na API com C: o bind de método destruidor não suporta parâmetros, o que pode ocorrer em alguns casos da GLib – lembre-se do que não há métodos destruidores reais em C, mas sim funções de desalocação.
[/update]


A única coisa que tenho a reclamar muito é a documentação precária, bem longe das documentações de API bem organizadas da SUN Microsystems e da Microsoft.

IDE


Por enquanto há uma IDE chamada Val(a)IDE – só que essa já desarmou na minha cara algumas vezes – e a promessa de uma extensão para o Eclipse – que ainda não apareceu na página da própria extensão.

Segundo a página official, há também extensões para MonoDevelop (Deus me livre!) e para GEdit.

**

É isso aí! Quando eu ganhar um pouco mais de intimidade com a linguagem, escrevo mais alguma coisa. Por enquanto foi só divulgação. Um tutorial meia-boca pode ser encontrado aqui.

[]'s
Cacilhas, La Batalema

quarta-feira, 28 de janeiro de 2009

Interface fluente

Outro pattern interessante é conhecido como carrying return (não confundir com carriage return, carácter com código ASCII 13).
[update 2009-01-29]Esse era o nome dado há uns anos atrás, quando conheci esse pattern. Porém ele não ganhou espaço entre os programadores na época e poucos continuaram usando (como eu). =P

Hoje em dia ele voltou com o nome Fluent Interface, Interface Fluente.

A propósito, troquei o título deste artigo para usar uma linguagem mais nova e mais fácil de ser reconhecida.

Valeu Walter e Lucas![/update]


Há outros nomes, mas não consigo encontrar no São Google, pois não é um pattern muito usado.

Parecido com Smalltalk


Imagine que você tem uma classe Java que recebe muitos parâmetros na instanciação:
Person person = new Person(
aString, aDate, anotherString, aLoad, aPerson, anotherPerson
);


A quantidade de argumentos pode explodir ao extremo. Python pode apresentar maior visibilidade quanto ao significado dos parâmetros:
person = Person(
name=aString,
birthdate=aDate,
id=anotherString,
load=aLoad,
father=aPerson,
mother=anotherPerson
)


Smalltalk, uma linguagem bem mais antiga do que Python ou Java, já tinha uma abordagem bem mais elegante:
person := Person new.
person
setName: aString;
setBirthdate: aDate;
setId: anotherString;
setLoad: aLoad;
setFather: aPerson;
setMother: anotherPerson.


É possível conseguir um código similar em Java usando acessores tipo setter:
Person person = new Person();
person.setName(aString);
person.setBirthdate(aDate);
person.setId(anotherString);
person.setLoad(aLoad);
person.setFather(aPerson);
person.setMother(anotherPerson);


Finalmente


Mas é possível obter um código ainda mais elegante usando o pattern em questão.

A ideia é simples: em vez dos acessores setter não terem retorno (void), basta eles retornarem this:
class Person {
private String name;
private Date birthdate;
private String id;
private Load load;
private Person father;
private Person mother;

public Person() {
name = null;
birthdate = null;
id = null;
load = null;
father = null;
mother = null;
}

public String getName() {
return name;
}

public Person setName(String name) {
this.name = name;
return this;
}

public Date getBirthdate() {
return birthdate;
}

public Person setBirthdate(Date birthdate) {
this.birthdate = birthdate;
return this;
}

public Load getLoad() {
return load;
}

public Person setLoad(Load load) {
this.load = load;
return this;
}

public Person getFather() {
return father;
}

public Person setFather(Person father) {
this.father = father;
return this;
}

public Person getMother() {
return mother;
}

public Person setMother(Person mother) {
this.mother = mother;
return this;
}
}


Agora podemos usar uma estrutura de código similar à de Smalltalk:
Person person = new Person()
.setName(aString)
.setBirthdate(aDate)
.setId(anotherString)
.setLoad(aLoad)
.setFather(aPerson)
.setMother(anotherPerson);


[]'s
Cacilhas, La Batalema

segunda-feira, 2 de abril de 2007

Classes singleton

Semestre passado estávamos conversando sobre singleton (veja também a página em inglês), que é quando queremos que uma determinada classe tenha somente uma instância, e esta instância seja retornada na tentativa de se criar novas instâncias.

Na época escrevi um artigo sobre o assunto e agora resolvi atualizá-lo aqui.

A idéia inicial era demonstrar como é implementado singleton em algumas linguagens e vou seguir novamente o mesmo princípio.

Java


Em Java singleton é feito «bloqueando» o construtor da classe e usando um método para intermediar esta requisição (aliás esta é a abordagem da maioria das linguagens):
class Conexao {
//Atributos

private static Conexao inst = null;

//Metodos
private Conexao() {
// O construtor é privado!

}

public static Conexao nova() {
// Este método fará as vezes do construtor
if (inst == null)
inst = new Conexao();
return inst;
}

}


Então, para criar uma conexão, não será usado new, mas o método nova():
Conexao conn = Conexao.nova();


Funciona. =)

O grande problema é que singleton não é transparente em Java. É preciso estar atento e não tentar usar new, que é a forma natural de se obter uma instância de classe. =P

Ruby


É extremamente simples fazer singleton em Ruby:
requires 'singleton'

class Conexao
include Singleton

end


Bastou um include! Isto é incrivelmente simples, prático. Quem me explicou seu funcionamento foi o Walter:

A solução de Ruby é parecida com Java e Python.

A inclusão do módulo singleton torna o construtor da classe privado. A instância é criada no momento de instanciação. Os métodos clone e dup retornam erro.

E isso tudo escondidinho, em 343 linhas de código invísiveis ao usuário :)


Python (usando herança)


Uma forma de fazer singleton em Python é usando herança. Assim podemos criar uma classe Singleton que implemente a funcionalidade (feature) desejada e herdar as demais classes dela.

Bem, em Python o construtor é __init__(), mas há um método que é chamado antes do construtor, que é __new__(), e funciona de forma muito parecida com new de Perl. Ele cria a nova instância e a retorna.

Na verdade, em Python quando fazemos:
a = X(b, c)


Por trás o que está acontecendo é:
a = X.__new__(X, b, c)
a.__init__(b, c)


Então podemos sobrescrever este método para termos singleton. A idéia é que, se já houver uma instância, o __new__() retorne esta instância em vez de uma nova:
class Singleton(object):

__inst = None

def __new__(cls, *args, **kw):
if cls.__inst is None:
cls.__inst = object.__new__(cls)
return cls.__inst

def __copy__(self):
return self

def __deepcopy__(self, memo=None):
return self


Então temos o atributo de classe privado __inst que contém nada (None) na criação da classe, mas depois será uma referência à instância única.

O método __new__() está preparado para receber argumentos genéricos (*args, **kw), mas não fará nada com eles – é problema do __init__(). A função do __new__() é verificar se já existe a instância única, se não existe, cria, depois retorna ela.

Podemos usar a herança desta forma:
class Conexao(Singleton):


Ou seja, funciona exatamente como em Ruby.

Agora vamos à crítica!

O grande problema aqui é justamente a sobrescrita um método geralmente esquecido…

Isso pode gerar uma série de problemas quando surge a herança múltipla ou quando o método é novamente sobrescrito – ou foi também sobrescrito por outra classe pai.

Para contornar estes problemas uma boa saída é o uso de metaclasses.

Python usando metaclasse


Em Python classes são objetos! São instâncias de type. Metaclasses são classes filhas de type, portanto suas instâncias também são classes.

Podemos criar uma metaclasse que tenha procedimentos que precedam o construtor e o chamem só se for preciso.

Assim as classes instância da metaclasse podem ser filhas de outras classes e possuir até herança múltipla sem problemas com sobrescrita de métodos – pois todo tratamento é realizado num escopo ainda mais protegido.

Numa metaclasse o método que precede o construtor das classes instância é – por motivos óbvios para programadores Python – __call__().

Vamos criar então a metaclasse! Vou chamar de unique em vez de singleton (Por quê? Porque eu gosto, ora pois!):
class unique(type):

def __init__(cls, name, base, dict):
super(unique, cls).__init__(name, base, dict)
cls.__inst = None
cls.__copy__ = lambda self: self
cls.__deepcopy__ = lambda self, memo=None: self

def __call__(cls, *args, **kw):
if cls.__inst is None:
cls.__inst = super(unique, cls).__call__(*args, **kw)
return cls.__inst


O construtor chama o construtor da classe pai (super) e então a única coisa diferente que ele faz é a criação de um atributo privado de classe __inst e de duas funções para tratar as cópias. Só que este atributo __inst é «mais privado» do que os normais. =)

Aqui o escopo para o atributo privado não é a classe, mas unique (a metaclasse). Podem imaginar o nível de proteção disso? É como em C++, só que funciona. =)

Bem, o método __call__() faz exatamente o mesmo que __new__() no exemplo que usa herança, só que de forma mais eficiente. A implementação disso poderia ser:
class Conexao(object):

__metaclass__ = unique



Simples! E sem os problemas que poderiam vir com o uso de herança.

Mas aí vem um espírito de porco qualquer e pergunta:

Peraí! E se eu quiser usar também outra metaclasse, como autoprop?

Ahá! Não é tão complicado assim! Veja só:
class Conexao(object):

class __metaclass__(autoprop, unique):
pass



Lua


Em Lua, assim como em Perl, o construtor é um método de classe, o que facilita muito nosso trabalho, pois cada instância deve ser criada nele.

Uma classe é uma metatabela cuja chave __index referencia a si mesma:
local Conexao = {}
Conexao.__index = Conexao


O construtor padrão (nu e cru) costuma ser:
function Conexao:new(o)
o = o or {}
return setmetatable(o, self)
end


Vamos então criar uma função unique (porque eu gosto!) que retorne uma classe cujo construtor retorne sempre a mesma instância (usando closure para isso):
local function unique()
local inst
local cmt = {}
cmt.__index = cmt

cmt.new = function(self, o)
if not inst then
o = o or {}
inst = setmetatable(o, self)
if self.__init then
inst.__init()
end
end
return inst
end

return cmt
end


Então podemos criar nossa classe Conexao assim:
local Conexao = unique()


Os demais métodos podem ser implementados normalmente e, caso seja necessário implementar alguma coisa no construtor, é possível implementando o método de instância __init().

[]'s
Rodrigo Cacilhas