Objetos en Perl 


F. Javier García Castellano
Web: http://genura.ugr.es/~javi, Mail: javi@geneura.ugr.es
IndiceInicioPerl

(C) GeNeura Team
Web: http://geneura.ugr.es, Mail: todos@geneura.ugr.es

1.- Uso de Clases y Objetos

Hasta ahora no lo hemos visto, pero Perl también soporta la Programación Orientada a Objetos (POO). Empezando por el principio, una clase es una colección de variables y de funciones que acceden a esas variables. Un objeto es una instanciación particular de una clase.

En Perl, casi todos los módulos son, en realidad, objetos. Veamos un ejemplo de uso de un objeto.

Ejemplo de uso de la clase persona:

  #!/usr/bin/perl

  #Usamos el módulo/clase Persona
  use Persona;

  #Creamos un objeto del tipo persona
  $uno= Persona->new();

  #Vamos a darle un nombre a ese objeto, usando el método nombre
  $uno->nombre("Richal");

  #También vamos a darle una edad, usando el método edad
  $uno->edad(23);

  #Mostramos la edad y el nombre
  print $uno->nombre." tiene ".$uno->edad. " años \n";

  #¿Y si hoy fuera su cumpleaños?
  $uno->cumple;

  #Mostramos la edad y el nombre
  print $uno->nombre." acaba de cumplir ".$uno->edad. " años \n";

En el ejemplo anterior, tenemos la clase Persona y para crear un objeto de este tipo de clase ($uno) se utiliza el método new, o sea, se usa el constructor de la clase. Posteriormente se han utilizado tres métodos de la clase (nombre, edad y cumple) que nos muestran/modifican la edad y el nombre de la clase Persona. También tenemos que decir que para crear un objeto de la clase Persona, se ha llamado directamente a dicha clase persona y es, por tanto, un método de clase. Los métodos de clase, también pueden ser llamados con la siguiente sintaxis:

  ...
  $uno=Persona::new("Persona");
  ...

En el siguiente ejemplo se utiliza la clase XML::XSLT (un módulo de Perl) para aplicar una hoja XSL a un documento XML

Ejemplo de aplicación de una hoja XSL a un documento XML:

#!/usr/bin/perl

#Uso la libreria para aplica XSLT a XML (clase XML/XSLT)
use XML::XSLT;

#Ficheros con los que vamos a trabajar
my $ficheroXML = "ejemplo.xml";
my $ficheroXSL = "ejemplo.xsl";


#Leemos el fichero XML
open ( IN, "<$ficheroXML" ) || die "\n No puedo abrir el fichero XML:$ficheroXML\n";
my $miXML = join("", <IN> );
close IN;

#Creamos un objeto XSLT (llamamos al constructor)
$miXSL = XML::XSLT->new ("$ficheroXSL", warnings => 1) || die "\n No puedo abrir el fichero XSL:$ficheroXSL\n";

#Aplicamos la XSL al XML (usamos el método transform)
$miXSL->transform ($miXML);

#Lo pasamos a una cadena (usamos el método toString)
$salida=$miXSL->toString;

#Lo mostramos por pantalla
print $salida;

#Liberamos la memoria ocupada por la XSL 
$miXSL->dispose ();

Si nos damos cuenta la forma de llamar a este constructor es un poco diferente a la vista en el primer ejemplo, eso se debe a que el objeto XSLT está dentro de la jerarquía XML.

2.- Como hacer nuestra propia clase

Lo primero, para construir nuestra propia clase, será darle un nombre. Dicho nombre también será el nombre del fichero (más la extensión .pm -de perl module-) donde tendremos la clase. Después tendremos que empezar la clase con:

Encabezado de la clase Persona (fichero Persona.pm)

  package Persona;
  use strict; #Nos ponemos serios

la primera línea, sirve para indicar, que es una clase lo que estamos creando, y la segunda línea nos dice que tenemos que ser estrictos a la hora de programar, es decir, que hay que declarar las variables antes de usarlas, como en casi todos los lenguajes de programación.

2.1- Constructor

Después tenemos que programar el constructor, que podemos llamar como nos apetezca, pero por convención se utiliza el nombre new. En el contructor, primero miramos la clase que es,

sub new {
    my $this=shift; #Cogemos la clase que somos o una referencia a la clase (si soy un objeto)
    my $class = ref($this) || $this; #Averiguo la clase a la que pertenezco
    ....
}

En el anterior trozo de código para averiguar la clase a la que pertenezco, se mira si es o no una referencia lo que se pasa. Esto es para que el constructor pueda ser llamado como un método de clase (como parámetro tiene el tipo de referencia que recibe) o como un método de objeto (como primer parámetro recibe el objeto).

Una vez que sabemos la clase que estamos contruyendo, declaramos las propiedades (variables de instancia) de la clase,

sub new {
     ....
     my $self={}; #Inicializamos la tabla hash que contendrá las var. de instancia (NOMBRE Y EDAD)
     $self ->{NOMBRE} =undef ; #La clase Persona nace sin nombre  
     $self ->{EDAD}   =0 ;     #La clase Persona se construye con 0 años
     ....
}

En el anterior trozo de código, estamos usando una referencia (de ahí la notación con -> ) a un array asocitivo. De esta forma podremos darle nombres a las propiedades del objeto. También podríamos haber usado una referencia a un array indexado (con $self=[];), pero la forma de acceder a las propiedades sería mediante índices:

     ....
     $self=[];  
     $self ->[0] =undef ; #La clase Persona nace sin nombre  
     $self ->[1] =0 ;     #La clase Persona se construye con 0 años
     ....

Una vez averiguada la clase que somos y declaradas las variables de instancia, Perl no tiene que dar el visto bueno a la clase (bendecirla) y al final, del constructor, devolvemos la clase creada.

sub new {
     ....
     bless $self, $class; #Perl nos tiene que dar el visto bueno (bendecirla)
     return ($self); #Devolvemos la clase recién construida
  }

También podríamos haber bendecido sólo el objeto en sí (bless $self;), pero si lo hacemos así no sabremos a que clase pertenece y no se podrá heredar de él.

De esta forma el contructor nos queda como sigue:

sub new {
    my $this=shift; #Cogemos la clase que somos o una referencia a la clase (si soy un objeto)
    my $class = ref($this) || $this; #Averiguo la clase a la que pertenezco

    my $self={}; #Inicializamos la tabla hash que contendrá las var. de instancia (NOMBRE Y EDAD)
    $self ->{NOMBRE} =undef ; #La clase Persona nace sin nombre  
    $self ->{EDAD}   =0 ;     #La clase Persona se construye con 0 años

    bless $self, $class; #Perl nos tiene que dar el visto bueno (bendecirla)
    return ($self); #Devolvemos la clase recién construida
  }

2.2- Métodos de la clase y destructor

También tendremos que construir nuestros métodos para la clase. Para ello sólo tenemos que tener en cuenta que un método de una clase es simplemente una función (o subrutina) que toma como primer parámetro el tipo de clase a la que pertenece el método.

Podemos hacer el destructor (no obligatorio), que es un método, análogo a los anteriores, pero con la única diferencia en que se debe llamar DESTROY o END

Para terminar ponemos un 1; esto es así para que Perl pueda interpretar la clase de forma independiente y con esto no hace nada, es "sólo" un script que devuelve 1.

2.3- Clase Persona (ejemplo)

Como resultado ya tenemos nuestra propia clase.

Clase Persona (fichero Persona.pm)

  package Persona;
  use strict; #Nos ponemos serios


  ######################################################################
  #Constructor de la clase
  #

  sub new {
     my $this=shift; #Cogemos la clase que somos o una referencia a la clase (si soy un objeto)
     my $class = ref($this) || $this; #Averiguo la clase a la que pertenezco

     my $self={}; #Inicializamos la tabla hash que contendrá las var. de instancia (NOMBRE Y EDAD)
     $self ->{NOMBRE} =undef ; #La clase Persona nace sin nombre  
     $self ->{EDAD}   =0 ;     #La clase Persona se construye con 0 años

     bless $self, $class; #Perl nos tiene que dar el visto bueno (bendecirla)
     return ($self); #Devolvemos la clase recién construida
  }
  
  ######################################################################
  #Métodos de acceso a los datos de la clase
  #

  #metodo para ver/cambiar el nombre
  sub nombre{
       my $self=shift; #El primer parámetro de un metodo es la  clase
 
       #Miramos si se le ha pasado algún parámetro, en cuyo caso será el nombre
       $self->{NOMBRE}=shift if (@_);

       #Devolvemos el nombre
       return $self->{NOMBRE};
  }

  #metodo para ver/cambiar la edad
  sub edad{
       my $self=shift; #El primer parámetro de un metodo es la  clase
 
       #Miramos si se le ha pasado algún parámetro, en cuyo caso será la edad
       $self->{EDAD}=shift if (@_);

       #Devolvemos el nombre
       return $self->{EDAD};
  }

  ######################################################################
  #Métodos de la clase
  #

  #metodo para cumplir años
  sub cumple{
       my $self=shift; #El primer parámetro de un metodo es la  clase
 
       #Incrementamos la edad
       $self->{EDAD}++;

       #Devolvemos el nombre
       return $self->{EDAD};
  }


  ######################################################################
  #Destructor
  #

  sub DESTROY {
        my $self=shift; #El primer parámetro de un metodo es la  clase
        delete ($self->{NOMBRE});  
        delete ($self->{EDAD});  
  }

  #Fin
  1;

2.4- Herencia

Para realizar la herencia, tan sólo tenemos que añadir al array @ISA una lista de antecesores (si solo hay uno será herencia simple y si hay más es herencia múltiple). Cuando el intérprete de Perl no encuentre un método en la clase, buscará dicho método en la primera clase de la que hereda, después en la segunda, etc.

Clase Trabajador (fichero Trabajador.pm)

  package Trabajador;

  #Heredamos de Persona
  use Persona;
  @ISA=qw(Persona);

  use strict; #Nos ponemos serios


  #Añadimos un método con el salario del trabajador
  sub salario{
       my $self=shift; #El primer parámetro de un metodo es la  clase

       #Miramos si se le ha pasado algún parámetro, en cuyo caso será la edad
       $self->{SALARIO}=shift if (@_);

       #Devolvemos el nombre. Si nunca se ha modificado devolvera una cadena vacía.
       return $self->{SALARIO};
  }

  #Fin
  1;

Si quisieramos utilizar un método de la primera clase padre, bastaría con llamarla. Si en el anterior ejemplos quisieramos que la propiedad SALARIO se inicializase a un valor por defecto, tendríamos que crear un constructor que llamase al constructor de la clase Persona.

Clase Trabajador con constructor redefinido

  package Trabajador;

  #Heredamos de Persona
  use Persona;
  @ISA=qw(Persona);

  use strict; #Nos ponemos serios

  sub new {
     my $this=shift; #Cogemos la clase que somos o una referencia a la clase (si soy un objeto)
     my $class = ref($this) || $this; #Averiguo la clase a la que pertenezco

     my $self=$class->Persona::new("Persona"); #Inicializamos las propiedades con las usadas en Persona
     $self ->{SALARIO} = 0 ; #Inicializamos el salario

     bless $self, $class; #Perl nos tiene que dar el visto bueno (bendecirla)
     return ($self); #Devolvemos la clase recién construida
  }


  #Añadimos un método con el salario del trabajador
  sub salario{
       my $self=shift; #El primer parámetro de un metodo es la  clase

       #Miramos si se le ha pasado algún parámetro, en cuyo caso será la edad
       $self->{SALARIO}=shift if (@_);

       #Devolvemos el nombre. Si nunca se ha modificado devolvera una cadena vacía.
       return $self->{SALARIO};
  }

  #Fin
  1;

3. Ejercicios

Ejercicio: Construye tu propia clase (Alumno), para guardar datos de alumnos y notas. Haz también un programa que pruebe la clase.