2011-03-09 10 views
17

¿Cuál es la forma correcta de llamar al constructor base desde el constructor de la clase en Perl?Constructor de base de llamadas en Perl

he visto sintaxis como esta:

my $class = shift; 
my $a = shift; 
my $b = shift; 
my $self = $class->SUPER::new($a, $b); 
return $self; 

¿Es esto correcto? ¿Qué pasa si tenemos varias clases para padres? Por ejemplo, una clase como esta:.

package Gamma; 
use base Alpha; 
use base Beta; 

sub new 
{ 
    # Call base constructors... 
} 
1; 

Respuesta

18

Este problema es por qué algunas personas recomendamos no hacer nada interesante en su método new. new se espera que cree y devuelva una referencia bendecida, es difícil crear un sistema que maneje hacer esto dos veces para el mismo objeto de diferentes clases principales.

Una opción más limpia es tener un nuevo método que solo crea el objeto y llama a otro método que puede configurar el objeto. Este segundo método puede comportarse de una manera que permita llamar a varios métodos principales. Efectivamente new es su asignador y este otro método es su constructor.

package Mother; 
use strict; 
use warnings; 

sub new { 
    my ($class, @args) = @_; 
    my $self = bless {}, $class; 
    return $self->_init(@args); 
} 

sub _init { 
    my ($self, @args) = @_; 

    # do something 

    return $self; 
} 

package Father; 
use strict; 
use warnings; 

sub new { 
    my ($class, @args) = @_; 
    my $self = bless {}, $class; 
    return $self->_init(@args); 
} 

sub _init { 
    my ($self, @args) = @_; 

    # do something else 

    return $self; 
} 

package Child; 
use strict; 
use warnings; 

use base qw(Mother Father); 

sub _init { 
    my ($self, @args) = @_; 

    # do any thing that needs to be done before calling base classes 

    $self->Mother::_init(@args); # Call Mother::_init explicitly, SUPER::_init would also call Mother::_init 
    $self->Father::_init(@args); # Call Father::_init explicitly, SUPER::_init would NOT call Father::_init 

    # do any thing that needs to be done after calling base classes 

    return $self; 
} 

Ether tiene razón sobre las complicaciones que probablemente encuentre al usar herencia múltiple. Aún necesita saber que Father::_init no anulará ninguna decisión tomada por Mother::_init para comenzar. Solo se volverá más complicado y más difícil de depurar desde allí.

Recomendaría la recomendación de Moose, su interfaz incluye una mejor separación de la creación del objeto y la inicialización que mi ejemplo anterior que normalmente solo funciona.

+0

, gracias, esta parece ser la forma más fácil de convertir mi código actual a algo que funciona. – Zitrax

30

Si todo su constructor está haciendo es una llamada al constructor padre (como en el ejemplo, no es necesario escribir en absoluto simplemente dejarlo fuera y el padre ser llamado; sólo tiene que asegurarse de que el objeto ha sido bendecida en el tipo correcto:

package Parent; 
use strict; 
use warnings; 

sub new 
{ 
    my ($class, @args) = @_; 

    # do something with @args 

    return bless {}, $class; 
} 
1; 

si se utiliza el código anterior y tener una clase Child declara con use parent 'Parent'; continuación, el constructor de Padres construirá adecuadamente a un niño

.

If es necesario agregar algunas propiedades en el niño, entonces lo que tenía es en gran medida correcta:

package Child; 
use strict; 
use warnings; 

use parent 'Parent'; 

sub new 
{ 
    my ($class, @args) = @_; 

    # possibly call Parent->new(@args) first 
    my $self = $class->SUPER::new(@args); 

    # do something else with @args 

    # no need to rebless $self, if the Parent already blessed properly 
    return $self; 
} 
1; 

Sin embargo, cuando lleva a la herencia múltiple en la mezcla, que necesidad de decidir lo que hay que hacer en cada paso del camino. Esto significa un constructor personalizado para cada clase que decide cómo fusionar las propiedades de Parent1 y Parent2 en el elemento secundario y, finalmente, bendice el objeto resultante en la clase Child. Esta complicación es una de las muchas razones por las que la herencia múltiple es una mala elección de diseño. ¿Ha considerado rediseñar su jerarquía de objetos, posiblemente moviendo algunas propiedades a roles? Además, es posible que desee emplear un marco de trabajo de objetos para llevar a cabo parte del trabajo ocupado, como Moose. Hoy en día, rara vez es necesario escribir un constructor personalizado.

(Por último, se debe evitar el uso de las variables $a y $b, sino que son tratados de manera diferente en Perl, ya que son las variables utilizadas en funciones de clasificación y algunos otros muebles empotrados.)

5

Al usar herencia múltiple, el orden de resolución de método predeterminado es inferior a la media.Recomiendo que se agrega

use mro 'c3'; 

a los módulos y que se llame a la siguiente constructor de la cadena usando

sub new { 
    my ($class) = @_; 
    return $class->next::method(@_); 
} 

Alfa y Beta tendría que hacer lo mismo para que esto funcione.

Ref: mro

Cuestiones relacionadas