package Biblio::Doc;
use strict;
{
my $_count = 0;
sub get_count { $_count }
sub _incr_count { $_count++ }
}
sub new {
my $class = shift;
my ($identifier, $author, $publisher, $title, $year, $url) = @_;
$class->_incr_count();
my $paper = {
_identifier => $identifier,
_author => $author,
_publisher => $publisher,
_title => $title,
_year => $year,
_url => $url
};
bless $paper, $class;
}
sub get_identifier { $_[0]->{_identifier} }
sub get_author { $_[0]->{_author} }
sub get_publisher { $_[0]->{_publisher} }
sub get_title { $_[0]->{_title} }
sub get_year { $_[0]->{_year} }
sub get_url { $_[0]->{_url} }
sub set_url {
my ($self, $url) = @_;
$self ->{_url} = $url if $url;
return ($self ->{_url});
}
1;
La clase Biblio::Doc se implanta a través del package
Biblio::Doc
package Biblio::Doc; use strict; ...
Los paquetes proporcionan un conjunto de características que son comunes a las clases: proporcionan un espacio de nombres separado, agrupando así un código que ofrece un conjunto de funcionalidades relacionadas y aislándolo del resto; tienen un nombre que puede ser usado para identificar los datos y subrutinas en el paquete (fully qualified names) y, si el paquete es un módulo, diferencian entre la parte a exportar y la parte interna. Por todo ello Perl usa los paquetes para implantar las clases.
Obsérvese la clausura creada para
el contador de referencias bibliográficas $_count.
Este contador es el único atributo de clase en
este ejemplo. Los restantes son atributos del objeto.
{
my $_count = 0;
sub get_count { $_count }
sub _incr_count { $_count++ }
}
Es un convenio en Perl que las variables que comienzan con
un guión bajo son variables privadas. Consecuencia de
este convenio es que debemos entender que el autor pretende que
$_count y _incr_count sean privadas.
La variable privada $_count
contiene el contador de documentos.
Su acceso queda restringido no ya al ámbito del módulo
sino al ámbito léxico establecido por la clausura. La única forma
de acceder a $_count es a través de las funciones
_incr_count y get_count.
En general, en programación orientada a objetos se aconseja que se envuelvan todos los accesos a atributos en subrutinas y se prohiba la posible modificación exterior de los mismos ([18] .
A continuación viene el constructor de objetos
new (su nombre es arbitrario, pero la costumbre es llamar
así a los constructores)
de la clase Biblio::Doc
1 sub new {
2 my $class = shift;
3 my ($identifier, $author, $publisher, $title, $year, $url) = @_;
4 $class->_incr_count();
5
6 my $paper = {
7 _identifier => $identifier,
8 _author => $author,
9 _publisher => $publisher,
10 _title => $title,
11 _year => $year,
12 _url => $url
13 };
14
15 bless $paper, $class;
16 }
Un objeto Perl es un dato que ha sido bendecido con un paquete. La operación de bendición bless toma como primer argumento una referencia y como segundo un nombre de paquete.
El argumento ''nombre del paquete'' es opcional: se usará el nombre del package actual si se omite.
Una vez que un dato es ''bendecido'' (¿Quizá deberíamos decir ''bautizado como miembro de la clase''?) el dato pasa a ''saber'' a que clase/package pertenece.
En el ejemplo se bendice una referencia a un hash. Sin embargo, Perl admite que se bendiga una referencia a cualquier otro tipo de objeto.
Podremos crear el objeto con una llamada como la siguiente:
$obj = Biblio::Doc->new(1, "Asimov", "Bruguera", "Los propios dioses", 1989, "unknown");o bien usando esta otra sintáxis:
$obj = new Biblio::Doc(1, "Asimov", "Bruguera", "Los propios dioses", 1989, "unknown");
Lo que sucede cuando Perl ve una llamada a una
subrutina seguida del nombre de un paquete (new Biblio::Doc)
o una llamada con la sintáxis de paquete seguido de flecha
seguido de un método (Biblio::Doc->new)
es que llama a una subrutina con ese nombre en ese paquete,
y le pasa como primer argumento el nombre del
paquete.
Así, el primer argumento que recibe
el constructor new
es el nombre de la clase, esto es Biblio::Doc.
Así pues la línea 2
my $class = shift;Obtiene la clase
Biblio::Doc.
La llamada de la línea 4:
$class->_incr_count();es, por tanto, otro ejemplo de esta forma de llamada. En este caso tenemos además un referenciado simbólico via
$class.
El hash referenciado por $paper
en las líneas de la 6 a la 13
es bendecido en la línea 15 con el nombre de la clase.
1 sub new {
2 my $class = shift;
3 my ($identifier, $author, $publisher, $title, $year, $url) = @_;
4 $class->_incr_count();
5
6 my $paper = {
7 _identifier => $identifier,
8 _author => $author,
9 _publisher => $publisher,
10 _title => $title,
11 _year => $year,
12 _url => $url
13 };
14
15 bless $paper, $class;
16 }
Esto establece un campo interno dentro de la representación
interna del hash anónimo. En cierto sentido es %$paper
quien es bendecido, no $paper. La figura 6.1
ilustra este punto:
En la izquierda de la figura aparecen la referencia y el
referente antes de la bendición. En la parte de la derecha, después de ejecutar
bless $paper, $class. Obsérvese que se ha añadido
una marca con el nombre de la clase-paquete que identifica la estructura
de datos como un objeto de esa clase.
Una vez que el dato apuntado por la referencia ha sido bendecido,
el operador ref aplicado a dicha referencia devuelve el
nombre del paquete (esto es, el nombre de la clase) en vez
de HASH.
Una comprobación de que quien es bendecido es el dato referido no la referencia la da el siguiente código:
$obj = new Biblio::Doc(1, "Asimov", "Bruguera", "Los propios dioses", 1989, "unknown"); $obj2 = $obj; print "obj2 es un ",ref($obj2),"\n";
Se imprimirá que obj2 es un Biblio::Doc
La función reftype en Scalar::Util permite obtener el tipo básico subyacente al objeto:
DB<1> use Scalar::Util qw{reftype}
DB<2> $x = bless {}, 'SomeClass'
DB<3> p ref($x)
SomeClass
DB<4> p reftype($x)
HASH
Para escribir un método para una clase (package) dada, basta con escribir la correspondiente subrutina dentro del paquete.
Así el método get_author queda descrito mediante la subrutina:
sub get_author { $_[0]->{_author} }
Sin embargo, llamar a Biblio::Doc::get_author como método
conlleva una extensión a la sintáxis flecha de Perl. Si se tiene
que $obj es una referencia a un objeto de la clase
Biblio::Doc, podemos acceder a cualquier método del
paquete usando la notación $obj->method donde
method es cualquier método de la clase.
Por ejemplo:
$obj->get_author()Es equivalente a
Biblio::Doc::get_author($obj);Esto es: cuando una subrutina es llamada como método, la lista de argumentos que recibe comienza con la referencia al objeto a través del cual fué llamada y va seguida de los argumentos que fueron explicitados en la llamada al método.
Observe que la última llamada trata a get_author como una subrutina
ordinaria, mientras que la primera la usa ''como método''.
La sintáxis ''flecha'' para la llamada a un método es consistente
con el uso de la flecha para otros tipos de referencias en Perl. Así,
al igual que escribimos $hsh_ref->{clave} o bien
$sub_ref->(4,7,8) también escribimos $obj_ref->metodo(@args).
Una diferencia de esta última notación con las anteriores estriba
en que el referente tiene diferentes conductas, esto es, tiene varios
metodos y la conducta concreta que queremos producir se especifica
escribiendo el nombre del método después de la flecha.
Un método que es invocado a través de un objeto vía
$obj_ref->metodo(@args) (por oposición a un
objeto de clase que es aquel que
es invocado vía una clase/paquete Class->metodo(@args))
se denomina un método dinámico
o método de objeto.
Nótese el uso de los prefijos get_ y set_ utilizados
para establecer el tipo de acceso al atributo.
Un método a través del cual se accede a un atributo recibe el nombre de
accesor. Un método que permite modificar el
estado interno de un atributo se denomina mutator.
Un programador C
se sentiría tentado de usar prototipos para especificar mas claramente
el tipo de acceso a un método.
Perl no comprueba prototipos cuando una
subrutina de paquete es llamada como método, utilizando
la sintáxis $objref->method(@args).
En el caso mas general
resulta imposible conocer hasta el momento de la ejecución que subrutina
debe ser invocada en respuesta a una llamada de un método.
Es posible usar la sintáxis de flecha con una especificación de nombre completa.
nereida:~> perl -wde 0
main::(-e:1): 0
DB<1> package Tutu::Titi; sub new { bless {}, $_[0] }
DB<2> p __PACKAGE__
main
DB<3> x X->Tutu::Titi::new
0 X=HASH(0x8444024)
empty hash
La función blessed en Scalar::Util tiene la sintáxis:
blessed EXPR
Si EXPR es una referencia que fué bendecida
retorna el nombre del corespondiente paquete.
En otro caso retorna undef.
$scalar = "foo"; $class = blessed $scalar; # undef $ref = []; $class = blessed $ref; # undef $obj = bless [], "Foo"; $class = blessed $obj; # "Foo"