Orientação a objetos em Perl
Uma das linguagens mais poderosas que há é Perl. Criada por Larry Wall em 1987 com base em C, Lisp, AWK e sed, Perl se tornou a base de diversas linguagens modernas, como Python e Ruby.
Possui uma abordagem sintática ligeiramente diferente, resultante de seu lema: «há mais de uma forma de fazer algo». Sua tipagem estupidamente fraca (só três tipos básicos, escalar, vetor e vetor associativo ou hash, mais alguns menos usados, como soquete) ajuda mais do que atrapalha, devido a sua abordagem, o que deixa os defensores da tipagem forte vermelhos de raiva.
Só que a implementação Perl de orientação a objetos é no mínimo excêntrica. Não usei outro adjetivo, como «alienígena», ou «transcendental», porque prefiro reservar estes para a implementação Perl de multithreading (vide as manpages perlthrtut e perlothrtut).
Há uma reformulação de POO para Perl chamada reform, mais parecida com Ruby, mas ainda não consegui fazer funcionar direito.Em Perl, as classes são chamadas «pacotes», e são declaradas com o operador
package (óbvio =P). O final do pacote é onde termina o arquivo ou onde é declarado um novo pacote. Caso o programador queira começar o programa em si no mesmo arquivo onde são criados os pacotes, basta declarar o início do pacote main:package main;Mas a forma mais elegante é criar um pacote
.pm (por exemplo, exemplo.pm) e importanto o pacote no arquivo .pl (use exemplo;).O método construtor é chamado
new e o destruidor DESTROY (com letras maiúsculas mesmo). Há um procedimento padrão no construtor, que deve ser executado invariavelmente: (1) determinar a classe, (2) criar a instância, (3) transformar a instância numa instância «real» da classe (com o operador bless) e (4) retornar a instância pronta.Alguns passos podem ser executados simultaneamente.
Vamos a um exemplo: a criação de uma classe
Pessoa, com os atributos nome e ano_nasc (ano de nascimento):package Pessoa;
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {
nome => shift,
ano_nasc => shift
};
return bless $self, $class;
}Funções em Perl recolhem os parâmetros no vetor especial
@_. No caso de métodos, o primeiro argumento é a instância escopo do método, assim como Python.O operador
shift retorna o «próximo» elemento do vetor – não sendo informado qual vetor, o padrão é @_. Se for a primeira execução de shift, retorna o primeiro elemento, se for a segunda, retorna o segundo elemento, e assim por diante.Assim,
$this recebe o primeiro argumento, que é o escopo de execução, ou seja, uma representação da própria classe (diferente de todos os demais métodos =P). O próximo comando refina este dado.A declaração de
$self cria escopo escalar semelhante a um hash, cujas chaves são nome e ano_nasc, e os valores são respectivamente o segundo e o terceiro elementos de @_.O último comando faz duas coisas: transforma
$self em uma instância verdadeira de $class (bless) e retorna a instância (return).Bem, se você não conseguiu acompanhar até aqui, aconselho voltar lá em cima e começar de novo. =P
Vamos agora criar dois métodos! Um para retornar
nome e outro para retornar a idade (calculada a partir de ano_nasc). Primeiro o nome:sub nome {
my $self = shift;
my $v = shift;
$self->{nome} = $v if ($v);
return $self->{nome};
}O primeiro comando atribui o escopo atual (a instância) a
$self, depois a variável escalar $v recebe o próximo argumento (se não houver, recebe undef, equivalente a null de Java). Se foi passado algum parâmetro, este será atribuído à chave (atributo) nome, depois o valor deste é retornado.Em suma,
$inst->nome retorna nome. =)Agora vamos à idade:
sub idade {
my $self = shift;
my (
$seg, $min, $hora,
$dia, $mes, $ano,
$dsem, $dano, $isdst
) = localtime(time);
return (1900 + $ano - self->{ano_nasc});
}Novamente o primeiro comando atribui o escopo atual (a instância) a
$self.O próximo comando parece meio estranho, mas é só olhar melhor:
localtime(time) retorna a hora local na forma de um vetor de nove posições, onde as três primeiras são a hora e as três seguintes a data (as três últimas também não são complicadas, e são até bem úteis). Assim conseguimos atribuir o número do ano a $ano.Só que o número do ano em Perl é dado a partir de 1900! Então 2006 é retornado como
106.Por último é retornada a diferença entre o ano atual (
1900 + $ano) e o ano de nascimento atribuído à instância (ano_nasc).Herança
Agora a coisa fica melhor!
Os defensores de linguagens orientadas a objetos dizem que Perl não é orientado a objetos e pronto, principalmente os programadores C++ e Java. Estranho isso.
Por que estranho?
Porque Perl além de suportar orientação a objetos, suporta herança múltipla, algo que Java não suporta. =P
Em Perl, a herança (simples ou múltipla) é implementada através do vetor
@ISA.Como exemplo, será criado um pacote
Retangulo, com o método desenhar e as chaves (atributos) altura e largura, e será criado um pacote filho Quadrado:(¡NOVO! Corrigi alguns erros de digitação no código seguinte que poderiam impedir o funcionamento do programa ¡NOVO!)
package Retangulo;
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $self = {
altura => shift,
largura => shift
};
return bless $self, $class;
}
sub desenhar {
my $self = shift;
foreach (1 .. $self->{altura}) {
foreach (1 .. $self->{largura}) {
print '(*)';
}
print "\n";
}
}
package Quadrado;
@ISA = qw/ Retangulo /;
sub new {
my $this = shift;
my $class = ref($this) || $this;
my $lado = shift;
my $self = new Retangulo($lado, $lado);
return bless $self, $class;
}
1[update 2008-09-09]Fica mais claro fazendo assim:[/update]package Quadrado;
use base qw/ Retangulo /;
…
Não há nada de novo no pacote
Retangulo. Talvez o foreach, que repete o loop para cada elemento do vetor informado (o elemento atual é atribuído à variável especial $_). Veja a manpage perlintro.O pacote interessante é
Quadrado. Logo de cara temos a atribuição de @ISA. O operador qw «quebra» a string fornecida (entre / /) em um vetor, onde cada palavra (qw = queue of words, lista de palavras) é um elemento. É aconselhável usar qw sempre que for feita uma atribuição a @ISA.Assim,
Retangulo foi atribuído a @ISA de Quadrado, tornando este filho do primeiro.O construtor é bastante simples, nada diferente do que foi visto até aqui – obs.:
$lado está recebendo o primeiro parâmetro (segundo argumento, o primeiro é o escopo).A operação mais diferente é a atribuição de
$self, que chama o construtor do pacote (classe) pai. Repare que, da forma como está sendo feito, as duas chaves (altura e largura) receberão o valor de $lado (definição de quadrado, lembra?).Para testar, salve este trecho de código como
formas.pm, então crie um arquivo teste.pl:#!/usr/bin/perl
use formas;
print "Qual o tamanho do lado do quadrado? ";
chomp($_ = <>);
my $sq = new Quadrado($_);
$sq->desenhar;
exit;Execute então o script:
$ perl teste.pl
Qual o tamanho do lado do quadrado? Informe um valor (5, ou 12, ou o que você quiser), pressione <Enter> e veja o resultado.
Espero que alguém tenha se empolgado para aprender Perl. =)
[]'s
Referências:
- The Perl Directory (antigo Perl Mongers)
- O'Relly Perl.com
- CPAN
- ActivePerl
