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