2010-06-17 20 views
5

Estoy intentando escribir un rol de singleton usando Perl y Moose. Entiendo que un módulo MooseX :: Singleton está disponible pero siempre hay resistencia cuando se requiere otro módulo CPAN para nuestro proyecto. Después de probar esto y tener un pequeño problema, me gustaría entender POR QUÉ mi método no funciona. El papel Singleton he escrito es el siguiente:Roles de Singleton en Moose

package Singleton; 
use Moose::Role; 

my $_singleInstance; 

around 'new' => sub { 
    my $orig = shift; 
    my $class = shift; 
    if (not defined $_singleInstance){ 
     $_singleInstance = $class->$orig(@_); 
    } 
    return $_singleInstance; 
}; 

sub getInstance 
{ 
    return __PACKAGE__->new(); 
} 

1; 

Esto parece funcionar encontrar cuando sólo una clase utiliza el papel Singleton. Sin embargo, cuando dos clases (ClassA y ClassB, por ejemplo) ambas consumen el rol de Singleton aparece, ya que ambas se refieren a una variable compartida $ _singleInstance. Si llamo a ClassA-> getInstance, devuelve una referencia a un objeto ClassA. Si llamo a ClassB-> getInstance algún tiempo después en el mismo script, devuelve una referencia a un objeto de tipo ClassA (aunque llamé claramente al método getInstance para ClassB). Si no uso un rol y realmente copio y pego el código del rol de Singleton en ClassA y ClassB, parece funcionar bien. ¿Que está pasando aqui?

+1

Te das cuenta de que envolver 'new' solo es pedir un mundo de dolor, ¿verdad? – Ether

Respuesta

3

Está guardando la instancia en todos los tipos, en lugar de usar una diferente para cada tipo de clase.

Esto exige el patrón de diseño de fábrica, por ejemplo:

package MyApp::Factory; 

my %instances; 

# intantiates an object instance if there is none available, 
# otherwise returns an existing one. 
sub instance 
{ 
    my ($class, $type, @options) = @_; 

    return $instances{$type} if $instances{$type}; 
    $instances{$type} = $type->new(@options); 
} 

Si realmente desea únicos, instale MooseX :: Singleton en lugar de rodar su propia - si nos fijamos en la fuente verá representa una gran cantidad de casos extremos. Sin embargo, desaconsejaría obligar a tus clases a ser solteras, ya que eso elimina el control de la clase. En su lugar, utilice una fábrica (como se indicó anteriormente), para que la persona que llama pueda decidir cómo construir la clase, en lugar de forzar a todos los consumidores a utilizar un solo uso.

1

Comparten la variable de instancia. Debe asignarlo dentro del paquete utilizando el rol.

# find storage for instance 
my $iref = \${ "${class}::_instance" }; 

# an instance already exists; return it instead of creating a new one 
return $$iref if defined $$iref; 

# no instance yet, create a new one 
... 
+2

Si vas a seguir esa ruta, usar una función de metaclase es probablemente mucho más sólido que monkeying con la tabla de símbolos. – friedo

3

Su $_singleInstance está en el ámbito léxico al bloque donde aparece, en este caso el conjunto Singleton paquete. Su modificador around forma un cierre sobre esta variable, lo que significa que ve el mismo$_singleInstance cada vez que se ejecuta, independientemente de la clase en la que esté compuesto.

Una forma sencilla de resolver esto sería para almacenar los únicos en un hash:

my %_instances; 

around 'new' => sub { 
    my $orig = shift; 
    my $class = shift; 
    if (not defined $_instances{$class}){ 
     $_instances{$class} = $class->$orig(@_); 
    } 
    return $_instances{$class}; 
}; 

Posiblemente una mejor manera sería la de establecer un papel metaclase personalizado que almacena la instancia singleton para cada clase que consume ese papel.

+0

Me imaginé que era un problema con la variable que tiene el alcance del rol.Esto fue un poco confuso porque __PACKAGE__ parece referirse al paquete que consume el rol, y no al paquete/rol "Sngleton". Tenía la esperanza de que las variables definidas en un rol se ubicaran en el paquete que las consumía y no en el paquete del rol. – mjn12

+1

Moose afecta la forma en que Perl analiza o entiende las variables. No hay una forma (fácil) de hacer que el comportamiento que está asumiendo aquí funcione, y estaría completamente fuera del alcance de Moose de todos modos. – perigrin

2

"entiendo un módulo MooseX :: Singleton está disponible, pero siempre hay resistencia cuando se requiere otro módulo CPAN para nuestro proyecto."

Esto es realmente algo que necesita ser tratado. Como dep, MX: Singleton es muy pequeño. ¿Cual es el problema? ¿Estás atrapado en un Perl compartido globalmente en un servidor compartido o similar? Si es así, debería mirar local :: lib, que está diseñado para facilitar que los desarrolladores individuales administren adecuadamente las dependencias de CPAN con un script Makefile.PL, como cualquier otro módulo de CPAN.

+1

Gracias por tomarse el tiempo para responder la pregunta, sin embargo, esto no es realmente útil. Pedí ayuda para entender por qué este código en particular no funcionaba Y reconocí la existencia de MooseX :: singleton para evitar exactamente este tipo de respuesta. – mjn12