NOME
perlboot - Tutorial de Orientação a Objetos para Iniciantes
DESCRIÇÃO
Se você não estiver familiarizado com objetos de outras linguagens, as demais documentações sobre objetos em Perl podem ser um pouco assustadoras, tal como perlobj,uma referência básica sobre o uso de objetos, e perltoot, que introduz os leitores às peculiaridades do sistema de orientação a objetos do Perl de modo tutorial.
Então, vamos realizar uma abordagem diferente, presumindo a inexistência prévia de experiência com objetos. Será mais fácil se você conhecer subrotinas (perlsub), referências (perlref et. seq.), e pacotes (perlmod), então se familiarize com estes conceitos primeiro se você já não o fez.
Se pudéssemos falar com os animais...
Deixemos os animais falar por um momento:
sub Cow::speak {
print "a Cow goes moooo!\n";
}
sub Horse::speak {
print "a Horse goes neigh!\n";
}
sub Sheep::speak {
print "a Sheep goes baaaah!\n"
}
Cow::speak;
Horse::speak;
Sheep::speak;
Isto resulta em:
a Cow goes moooo!
a Horse goes neigh!
a Sheep goes baaaah!
Nada espetacular aqui. Simples subrotinas, contudo de pacotes separados, e chamadas utilizando o nome completo do pacote. Então vamos criar um pasto inteiro:
# Cow::speak, Horse::speak, Sheep::speak as before
@pasture = qw(Cow Cow Horse Sheep Sheep);
foreach $animal (@pasture) {
&{$animal."::speak"};
}
Isto resulta em:
a Cow goes moooo!
a Cow goes moooo!
a Horse goes neigh!
a Sheep goes baaaah!
a Sheep goes baaaah!
Uau. Esta dereferenciação simbólica de coderefs aí é bastante indecente. Estamos contando com o modo no strict subs
, certamente não recomendado para programas maiores. E porque isto foi necessário? Porque o nome do pacote parece ser inseparável do nome da subrotina que queremos invocar dentro deste pacote.
Ou será que é?
Introduzindo a seta de invocação de métodos
Por enquanto, digamos que Class->method
invoca a subrotina method
no pacote Class
. (Aqui, "Class" é utilizada no sentido de "categoria", não no seu sentido "escolástico".) Esta afirmação não está completamente correta, mas vamos tomar um passo de cada vez. Agora vamos utilizar assim:
# Cow::speak, Horse::speak, Sheep::speak as before
Cow->speak;
Horse->speak;
Sheep->speak;
E novamente, isto resulta em:
a Cow goes moooo!
a Horse goes neigh!
a Sheep goes baaaah!
Ainda não está divertido. A mesma quantidade de caracteres, todos constantes, nenhuma variável. Porém, as partes agora são separáveis. Veja:
$a = "Cow";
$a->speak; # invokes Cow->speak
Ahh! Agora que o nome do pacote foi separado do nome da subrotina, podemos usar um nome de pacote variável. E desta vez, obtemos algo que funciona mesmo quando use strict refs
está habilitado.
Invocando um celeiro
Vamos pegar aquela nova seta de invocação e colocá-la de volta no exemplo do celeiro:
sub Cow::speak {
print "a Cow goes moooo!\n";
}
sub Horse::speak {
print "a Horse goes neigh!\n";
}
sub Sheep::speak {
print "a Sheep goes baaaah!\n"
}
@pasture = qw(Cow Cow Horse Sheep Sheep);
foreach $animal (@pasture) {
$animal->speak;
}
Pronto! Agora temos todos os animais falando, e seguramente, sem o uso de coderefs simbólicos.
Mas observe todo esse código em comum. Cada uma das rotinas speak
possuem estrutura similar: um operador print
e uma string que contém texto em comum. Seria legal se pudéssemos fatorar as comunalidades, no caso de decidirmos mudar para says
invés de goes
.
E na verdade nós temos uma forma de fazer isso sem muito alarde, mas temos que ouvir um pouco mais sobre o que a seta de invocação de métodos está realmente fazendo por nós.
O parâmetro extra de invocação de método
A invocação de:
Class->method(@args)
tenta invocar a subrotina Class::method
como:
Class::method("Class", @args);
(Se a subrotina não for encontrada, a "herança" entra em cena, mas falaremos sobre isso depois.) Isto significa que nós obtemos o nome da classe como o primeiro parâmetro (o único, se não for passado nenhum argumento). Então podemos reescrever a subrotina falante de Sheep
como:
sub Sheep::speak {
my $class = shift;
print "a $class goes baaaah!\n";
}
E os outros animais saem de forma similar:
sub Cow::speak {
my $class = shift;
print "a $class goes moooo!\n";
}
sub Horse::speak {
my $class = shift;
print "a $class goes neigh!\n";
}
Em cada caso, $class
terá o valor apropriado para aquela subrotina. Porém, novamente, temos muita estrutura similar. Poderemos fatorar isso mais ainda? Sim, chamando outro método na mesma classe.
Chamando um segundo método para simplificar as coisas
Dentro de speak
, vamos chamar um método ajudante chamado sound
. Este método provê o texto constante para o som em sí.
{ package Cow;
sub sound { "moooo" }
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n"
}
}
Agora, quando chamarmos Cow->speak
, obtemos uma $class
de Cow
em speak
. Isto por sua vez seleciona o método Cow->sound
, que retorna moooo
. Mas quão diferente seria para para o Horse
?
{ package Horse;
sub sound { "neigh" }
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n"
}
}
Apenas o nome do pacote e a mudança específica do som. Então será que podemos compartilhar a definição de speak
entre a Cow e o Horse? Sim, através de Herança!
Herdando as gargantas
Definiremos um pacote de subrotinas em comum chamado Animal
, com a definição de speak
:
{ package Animal;
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n"
}
}
Em seguida, dizemos que cada animal herda de Animal
, junto com o som específico do animal:
{ package Cow;
@ISA = qw(Animal);
sub sound { "moooo" }
}
Observe o array adicional @ISA
. Chegaremos lá em um minuto.
Mas o que acontece quando invocamos <Core-
speak>> agora?
Primeiro, Perl constrói a lista de argumentos. Neste caso, somente Cow
. Então Perl procura por Cow::speak
. Perl checa pela herança do array @Cow::ISA
. Ele está lá e contêm o nome Animal
.
Perl checa o próximo por speak
em Animal
em vez de, como em Animal::speak
. And that's found, so Perl invokes that subroutine with the already frozen argument list.
Inside the Animal::speak
subroutine, $class
becomes Cow
(the first argument). So when we get to the step of invoking $class->sound
, it'll be looking for Cow->sound
, which gets it on the first try without looking at @ISA
. Success!
Um pequena nota sobre @ISA
Essa variável mágica @ISA
(pronunciada "is a" = "é um(a)" não "ice-uh"), tinha sido declarada que Cow
"is a" = "é um(a)" Animal
. Perceba que ele é um array, não um valor singular simples, porque nas raras ocasiões, faz sentido ter mais de uma classe pai que busca métodos não encontrados.
Se Animal
também tivesse um @ISA
, então nós teríamos checado lá também. A busca é recursiva, primeira extensão, esquerda para direita em cada<@ISA>. Normalmente, cada @ISA
tem apenas um elemento (múltiplos elementos significam múltiplas heranças e múltiplas dores de cabeças), o que nos dá uma boa árvore de herança.
Quando ativamos o use strict
, recebemos reclamações sobre o @ISA
, já que não é uma variável contendo um nome explícito de pacote, ou uma variável léxica ("my"). Mas não podemos torná-la uma variável léxica (ela precisa pertencer ao pacote para ser encontrada pelo mecanismo de herança), então há algumas formas mais diretas de se lidar com isso.
A forma mais fácil é explicitar o nome do pacote:
@Cow::ISA = qw(Animal);
Ou permiti-la como uma variável de pacote com nome implícito:
package Cow;
use vars qw(@ISA);
@ISA = qw(Animal);
Se você está trazendo a classe de fora, através de um módulo orientado a objetos, você muda:
package Cow;
use Animal;
use vars qw(@ISA);
@ISA = qw(Animal);
para apenas:
package Cow;
use base qw(Animal);
E isso é bastante compacto.
Sobrepondo métodos
Vamos adicionar um rato (mouse), que mal pode ser ouvido:
# Animal package from before
{ package Mouse;
@ISA = qw(Animal);
sub sound { "squeak" }
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n";
print "[but you can barely hear it!]\n";
}
}
Mouse->speak;
que resulta em:
a Mouse goes squeak!
[but you can barely hear it!]
Aqui, Mouse
possui sua própria rotina de fala, então Mouse->speak
não invoca imediatamente Animal->speak
. Isso é conhecido como "overriding". De fato, não precisávamos nem dizer que o Mouse
era um Animal
, já que todos os métodos usados pelo speak
foram completamente definidos em Mouse
.
Mas agora nós duplicamos parte do código de Animal->speak
, e isso pode novamente virar uma dor de cabeça na hora de manter. Então, será que podemos evitar isso? Poderíamos dizer de alguma forma que o Mouse
faz tudo que qualquer outro Animal
faz, mas adicionar o comentário extra? Claro!
Primeiro, nós podemos invocar o método Animal::speak
diretamente:
# Animal package from before
{ package Mouse;
@ISA = qw(Animal);
sub sound { "squeak" }
sub speak {
my $class = shift;
Animal::speak($class);
print "[but you can barely hear it!]\n";
}
}
Note que precisamos incluir o parâmetro $class
(quase com certeza com o valor "Mouse"
) como primeiro parâmetro para Animal::speak
, já que paramos de usar a seta de método. Por que paramos? Bem, se invocarmos Animal->speak
ali, o primeiro parâmetro para o método será "Animal"
e não "Mouse"
, e quando chegar a hora dele chamar sound
, ele não terá a classe certa para chegar a este pacote.
Mas chamar Animal::speak
diretamente é confuso e passível de erros. E se Animal::speak
não existisse antes, e estivesse sendo herdado de uma classe mencionada em @Animal::ISA
? Como não estamos usando a seta de método, temos uma e apenas uma chance de acertar a subrotina correta.
Note ainda que a classe Animal
está escrita explicitamente (hardcoded, hardwired) dentro da subrotina. Isso é péssimo se alguém mantém o código e muda o @ISA
do Mouse
e não percebe a chamada ao Animal
ali no speak
. Então essa provavelmente não é a melhor forma de se fazer.
Começando a busca por outro lugar
Uma solução melhor é dizer ao Perl para procurar em um nível mais alto na cadeia de herança:
# same Animal as before
{ package Mouse;
# same @ISA, &sound as before
sub speak {
my $class = shift;
$class->Animal::speak;
print "[but you can barely hear it!]\n";
}
}
Ahh. Isso funciona. Usando essa sintaxe, começamos com Animal
para encontrar speak
, e usamos toda a cadeia de herança de Animal
caso não o encontremos imediatamente. Além disso, o primeiro parâmetro será $class
, então o método speak
encontrado receberá Mouse
como sua primeira entrada, e chegará eventualmente de volta a Mouse::sound
para os detalhes.
Mas essa não é a melhor solução. Ainda temos que manter o @ISA
e o pacote de busca inicial coordenados. Pior ainda, se Mouse
tivesse múltiplas entradas em @ISA
, nós não necessariamente saberíamos qual delas realmente definiu speak
. Então, será que existe uma forma ainda melhor?
O jeito SUPER de fazer as coisas
Ao mudarmos a classe Animal
para a classe SUPER
naquela chamada, nossa busca ocorrerá em todas as nossas super-classes (classes listadas em @ISA
) automaticamente:
# same Animal as before
{ package Mouse;
# same @ISA, &sound as before
sub speak {
my $class = shift;
$class->SUPER::speak;
print "[but you can barely hear it!]\n";
}
}
Então, SUPER::speak
significa procurar no @ISA
do pacote atual por speak
, invocando o primeiro encontrado. Note que ele não olha no @ISA
de $class
.
Onde estamos até agora...
Até agora, vimos a sintaxe de seta para métodos:
Class->method(@args);
ou o equivalente:
$a = "Class";
$a->method(@args);
que constrói uma lista de argumentos contendo:
("Class", @args)
e tenta invocar
Class::method("Class", @Args);
No entanto, se Class::method
não for encontrado, então @Class::ISA
será examinado (recursivamente) para localizar o pacote que de fato contém method
, e essa subrotina será chamada no lugar.
Usando essa sintaxe simples, nós temos métodos para classes, herança (múltipla), sobreposição e extensão. Usando apenas o que vimos até agora, pudemos separar código comum, e oferecer uma boa forma de reutilizar implementações com variações. Isso está na essência do que objetos oferecem, mas objetos também fornecem dados de instância, que ainda nem começamos a ver.
Um cavalo é um cavalo e vice-versa. Ou não.
Vamos começar com o código das classes Animal
e Horse
:
{ package Animal;
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n"
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { "neigh" }
}
Isso nos permite invocar Horse->speak
para seguir até Animal::speak
, chamando de volta Horse::sound
para obter o som específico e a saída de:
a Horse goes neigh!
Mas todos os nossos objetos Horse
teriam que ser completamente idênticos. Se eu adicionar uma subrotina, todos os cavalos (horses) vão automaticamente compartilhá-la. Isso é ótimo para fazer todos os cavalos iguais, mas como podemos obter as diferenças entre cavalos distintos? Por exemplo, suponha que eu quero dar um nome ao meu primeiro cavalo. Deve haver uma forma de manter o nome separado do nome dos outros cavalos.
Podemos fazer isso através de uma nova distinção, chamada de "instância". Uma "instância" é normalmente criada por uma classe. Em Perl, qualquer referência pode ser uma instância, então vamos começar com a referência mais simples que pode armazenar o nome do cavalo: uma referência de escalar.
my $name = "Mr. Ed";
my $talking = \$name;
Então agora $talking
é uma referência ao que será um dado específico da instância (o nome). O último passo para transformar isso em uma instância de verdade é com o operador especial chamado bless
:
bless $talking, Horse;
Esse operador armazena informações sobre o pacote chamado Horse
dentro da coisa apontada pela referência. Agora dizemos que $talking
é uma instância de Horse
. Isto é, trata-se de um cavalo específico. Fora isso, não é mudanças quanto à referência, e ela ainda pode ser usada com operadores tradicionais de referência.
Invocando um método da instância
A seta de método pode ser usada em instâncias, assim como nomes de pacotes (classes). Então, vamos obter o som que $talking
faz:
my $noise = $talking->sound;
Para invocar sound
, o Perl primeiro percebe que $talking
é uma referência abençoada ("blessed") -- logo, uma instância. Ele então constrói uma lista de argumentos, nesse caso a partir apenas de $talking
. (Mais tarde veremos que os argumentos serão ordenados logo após a variável da instância, exatamente como nas classes).
Agora a parte divertida: Perl pega a classe em que a instância foi abençoada, nesse caso Horse
, e usa isso para localizar a subrotina que invocou o método. Nesse caso, Horse::sound
é encontrado diretamente (sem usar herança), gerando a chamada final:
Horse::sound($talking)
Note que o primeiro parâmetro aqui ainda é a instância, não o nome da classe como era antes. Teremos neigh
como valor de retorno, e isso irá para a variável $noise
acima.
Se Horse::sound
não tivesse sido encontrado, seguiríamos até a lista @Horse::ISA
para tentar encontrar o método em uma das superclasses, exatamente como o método de classe. A única diferença entre métodos de classe e métodos de instância é se o primeiro parâmetro é a instância (a referência abençoada) ou o nome da classe (uma string).
Acessando dados da instância
Como obtemos a instância como primeiro parâmetro, podemos agora acessar os dados específicos dela. Nesse caso, vamos adicionar uma forma de chegar até o nome:
{ package Horse;
@ISA = qw(Animal);
sub sound { "neigh" }
sub name {
my $self = shift;
$self;
}
}
Agora vamos chamar pelo nome:
print $talking->name, " says ", $talking->sound, "\n";
Dentro de Horse::name
, o array @_
contém apenas $talking
, que o shift
armazena dentro de $self
(é tradição para métodos de instância fazer um shift do primeiro parâmetro para uma variável chamada $self
, então faça desse jeito a menos que tenha fortes motivos para mudar). $self
é então desreferenciado para uma referência de escalar, gerando Mr. Ed
, e acabamos. O resultado é:
Mr. Ed says neigh.
Como construir um cavalo
Naturalmente, se fizéssemos todos os nossos cavalos à mão, provavelmente cometeríamos erros de vez em quando. Também estaríamos violando uma das propriedades de programação orientada a objetos, já que as "entranhas" do cavalo estariam visíveis. Isso é bom se você é um veterinário, mas não se você gosta apenas de ter cavalos. Então, vamos deixar a própria classe Horse
fazer novos cavalos:
{ package Horse;
@ISA = qw(Animal);
sub sound { "neigh" }
sub name {
my $self = shift;
$$self;
}
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
}
Agora com o novo método named
, podemos fazer um cavalo:
my $talking = Horse->named("Mr. Ed");
Note que estamos de volta em um método de classe, então os dois argumentos para Horse::named
são Horse
e Mr Ed
. O operador bless
não apenas abençoa $name
, mas também retorna a referência a $name
, então ele pode ser usado como valor de retorno sem problemas. E é assim que fazemos um cavalo.
Aqui, chamados nosso construtor de named
, para que ele denote o argumento do construtor como o nome deste cavalo em particular. Você pode usar construtores diferentes com nomes diferentes para formas diferentes de "dar a luz" ao objeto (como talvez registrando seu pedigree ou data de nascimento). No entanto, você vai perceber que a maioria das pessoas chegando ao Perl por linguagens mais limitadas usam apenas um construtor chamado new
. Tanto faz o estilo, contanto que você documente seu modo particular de "dar a luz" a um objeto (e você vai documentar, certo?).
Herdando o construtor
Havia algo específico a Horse
em nosso método? Não. Logo, a receita é a mesma para construir qualquer outra coisa que herde de Animal
, então vamos colocar lá:
{ package Animal;
sub speak {
my $class = shift;
print "a $class goes ", $class->sound, "!\n"
}
sub name {
my $$self = shift;
$self;
}
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { "neigh" }
}
Ahh, mas o que acontece se invocarmos speak
em uma instância?
my $talking = Horse->named("Mr. Ed");
$talking->speak;
Recebemos o valor de depuração:
a Horse=SCALAR(0xaca42ac) goes neigh!
Por quê? Porque a rotina Animal::speak
está esperando um nome de classe como primeiro parâmetro, e não uma instância. Quando a instância é passada, ele acaba usando a referência escalar abençoada como uma string simples, e é o que aparece como vimos agora.
Fazendo um método funcionar tanto com classes quanto com instâncias
Tudo que precisamos é que o método detecte se está sendo chamado pela classe ou por uma instância. A forma mais imediata para isso é através do operador ref
. Ele retorna uma string (o nome da classe) quando usado com uma referência abençoada, e undef
quando é usado numa string (como o nome da classe). Vamos primeiro modificar o método name
para notar a mudança:
sub name {
my $either = shift;
ref $either
? $$either # it's an instance, return name
: "an unnamed $either"; # it's a class, return generic
}
Aqui, o operador ?:
é útil para selecionar a desreferência ou uma string derivada. Agora podemos usar isso tanto com uma instância quanto com uma classe. Note que eu mudei o primeiro parâmetro para $either
(qualquer) para mostrar que essa é a intenção:
my $talking = Horse->named("Mr. Ed");
print Horse->name, "\n"; # prints "an unnamed Horse\n"
print $talking->name, "\n"; # prints "Mr Ed.\n"
e agora consertamos o método speak
para usar isso:
sub speak {
my $either = shift;
print $either->name, " goes ", $either->sound, "\n";
}
E como sound
já funcionava com classe ou instância, acabamos!
Adicionando parâmetros a um método
Vamos ensinar nossos animais a comer:
{ package Animal;
sub named {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
sub name {
my $either = shift;
ref $either
? $$either # it's an instance, return name
: "an unnamed $either"; # it's a class, return generic
}
sub speak {
my $either = shift;
print $either->name, " goes ", $either->sound, "\n";
}
sub eat {
my $either = shift;
my $food = shift;
print $either->name, " eats $food.\n";
}
}
{ package Horse;
@ISA = qw(Animal);
sub sound { "neigh" }
}
{ package Sheep;
@ISA = qw(Animal);
sub sound { "baaaah" }
}
Agora tente:
my $talking = Horse->named("Mr. Ed");
$talking->eat("hay");
Sheep->eat("grass");
Que exibe:
Mr. Ed eats hay.
an unnamed Sheep eats grass.
Um método de instância com parâmetros é chamado com a instância, e então com a lista de parâmetros. Assim, aquela primeira chamada é como se fosse:
Animal::eat($talking, "hay");
Instâncias mais interessantes
E se uma instância precisa de mais dados? Instâncias mais interessantes são feitas de vários itens, cada um podendo ser uma referência ou mesmo outro objeto. A forma mais fácil de armazenar isso costuma ser em um hash. As chaves do hash servem como nomes das partes do objeto (comummente chamadas "variáveis de instância"), e os valores correspondentes são, bem, os valores.
Mas como transformamos o cavalo em um hash? Lembre-se que um objeto é quaquer referência abençoada. Podemos criar uma referência de hash abençoada tão facilmente quanto a referência de escalar abençoada, desde que tudo que acessa a referência seja mudado de acordo.
Vamos fazer uma ovelha que possui um nome e uma cor:
my $bad = bless { Name => "Evil", Color => "black" }, Sheep;
então $bad->{Name}
tem o valor Evil
, e $bad->{Color}
tem black
. Mas queremos que $bad->name
acesse o nome, e isso agora está quebrado pois esperamos uma referência escalar. Não se preocupe, isso é bem fácil de consertar:
## in Animal
sub name {
my $either = shift;
ref $either ?
$either->{Name} :
"an unnamed $either";
}
E, claro, named
ainda constrói uma ovelha escalar, então vamos consertar isso também:
## in Animal
sub named {
my $class = shift;
my $name = shift;
my $self = { Name => $name, Color => $class->default_color };
bless $self, $class;
}
O que é esse default_color
? Bem, se named
tiver apenas o nome, ainda vamos precisar definir a cor, então botamos uma cor padrão para cada classe. Para uma ovelha, podemos definir como branco:
## in Sheep
sub default_color { "white" }
E para evitar termos que definir para cada uma das classes adicionais, vamos definir um método "matriz" que servirá como o "padrão padrão", diretamente em Animal
:
## in Animal
sub default_color { "brown" }
Agora, como name
e named
são os únicos métodos que referenciam a "estrutura" do objeto, o resto dos métodos pode permanecer o mesmo, e speak
vai funcionar exatamente como antes.
Um cavalo de cor diferente
Todos os nossos cavalos sendo marrons seria chato. Então vamos adicionar um ou dois métodos para obter e definir a cor:
## in Animal
sub color {
$_[0]->{Color}
}
sub set_color {
$_[0]->{Color} = $_[1];
}
Note o modo alternativo de acessar os argumentos: $_[0]
é usado diretamente, em vez de via shift
(Isso salva um pouco de tempo para algo que pode ser chamado frequentemente). E agora podemos consertar a cor do Mr. Ed:
my $talking = Horse->named("Mr. Ed");
$talking->set_color("black-and-white");
print $talking->name, " is colored ", $talking->color, "\n";
que resulta em:
Mr. Ed is colored black-and-white
Sumário
Então, agora temos métodos de classe, construtores, métodos de instância, dados de instância, até mesmo acessores. Mas isso é apenas o início do que Perl tem a oferecer. Ainda nem começamos a falar sobre acessores que servem tanto como getters quanto setters, destrutores, notação indireta a objetos, subclasses que adicionam dados de instância, dados por classe, overloading, testes "isa" e "can", a classe UNIVERSAL
, e muito mais. Isso cabe ao restante da documentação do Perl. Mas espero que isso seja o suficiente para você começar.
VEJA TAMBÉM
Para mais informações, veja perlobj (para todos os detalhes sórdidos sobre objetos em Perl, agora que você já viu o básico), perltoot (o tutorial para aqueles que já conhecem objetos), perltooc (lidando com dados de classe), perlbot (para alguns outros truques), e livros como o excelente Object Oriented Perl de Damian Conway.
Alguns módulos que podem ser interessantes são Class::Accessor, Class::Class, Class::Contract, Class::Data::Inheritable, Class::MethodMaker e Tie::SecureHash.
COPYRIGHT
Copyright (c) 1999, 2000 by Randal L. Schwartz and Stonehenge Consulting Services, Inc. Permission is hereby granted to distribute this document intact with the Perl distribution, and in accordance with the licenses of the Perl distribution; derived documents must include this copyright notice intact.
Portions of this text have been derived from Perl Training materials originally appearing in the Packages, References, Objects, and Modules course taught by instructors for Stonehenge Consulting Services, Inc. and used with permission.
Portions of this text have been derived from materials originally appearing in Linux Magazine and used with permission.
TRADUÇÃO
Eden Cardim
Breno G. de Oliveira
Joênio Costa Marques
Roan Brasil Monteiro
Nelson Ferraz
Daniel Mantovani
Marco Lima