2010-06-17 19 views
5

Ich versuche, eine Singleton-Rolle mit Perl und Moose zu schreiben. Ich verstehe, dass ein MooseX :: Singleton-Modul verfügbar ist, aber es gibt immer Widerstand, wenn ein anderes CPAN-Modul für unser Projekt benötigt wird. Nachdem ich das versucht habe und ein paar Probleme habe, möchte ich verstehen, warum meine Methode nicht funktioniert. Die Singleton-Rolle, die ich geschrieben habe, ist wie folgt:Singleton Rollen in 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; 

Dies scheint finden zu arbeiten, wenn nur eine Klasse die Singleton Rolle verwendet. Wenn jedoch zwei Klassen (z. B. ClassA und ClassB) beide die Singleton-Rolle verwenden, werden beide auf eine gemeinsam genutzte Variable $ _singleInstance verwiesen. Wenn ich ClassA-> getInstance aufruft, gibt es einen Verweis auf ein ClassA-Objekt zurück. Wenn ich ClassB-> getInstance irgendwann später im selben Skript aufruft, gibt es einen Verweis auf ein Objekt vom Typ ClassA zurück (obwohl ich die getInstance-Methode für ClassB eindeutig aufgerufen habe). Wenn ich keine Rolle verwende und den Code aus der Singleton-Rolle tatsächlich in ClassA und ClassB kopiere und einfüge, scheint es zu funktionieren. Was ist denn hier los?

+1

Sie erkennen, dass das Einpacken 'neu' nur nach einer Welt der Verletzung fragt, richtig? – Ether

Antwort

3

Sie speichern die Instanz über alle Typen, anstatt für jeden Klassentyp einen anderen zu verwenden.

Dies spricht für die Fabrik-Entwurfsmuster, zB:

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); 
} 

Wenn Sie wirklich Singletons wollen, installieren Sie bitte MooseX :: Singleton, anstatt Ihre eigene Fahrzeuge - wenn Sie die Quelle zu buchen, Sie werden sehen, es ist für viele Randfälle verantwortlich. Ich rate jedoch davon ab, Ihre Klassen dazu zu zwingen, Singletons zu sein, da dies die Kontrolle von der Klasse selbst entfernt. Verwenden Sie stattdessen eine Factory (wie oben), damit der Aufrufer entscheiden kann, wie die Klasse erstellt wird, anstatt alle Consumer in einen Anwendungsfall zu zwingen.

1

Sie teilen sich die Instanzvariable. Sie müssen es innerhalb des Pakets mithilfe der Rolle zuordnen.

# 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

Wenn Sie diese Route gehen, ist die Verwendung einer Metaklassenrolle wahrscheinlich robuster als das Affenging mit der Symboltabelle. – friedo

3

Ihre $_singleInstance ist lexikalisch zum Block scoped, wo es in diesem Fall Ihr gesamtes Singleton Paket erscheint. Ihr around Modifizierer bildet eine Schließung über diese Variable, was bedeutet, dass sie die gleiche$_singleInstance jedes Mal, wenn es ausgeführt wird, unabhängig davon, in welcher Klasse es zusammengesetzt wird.

Ein einfacher Weg, dies zu lösen zu speichern wäre Ihre Singletons in einem Hash: eine benutzerdefinierte metaclass Rolle

my %_instances; 

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

Möglicherweise eine bessere Möglichkeit wäre einrichten, die für jede Klasse, die verbraucht die Singleton-Instanz speichert diese Rolle.

+0

Ich dachte, dass es ein Problem mit der Variable war, die auf die Rolle beschränkt ist.Dies war nur ein wenig verwirrend, da sich __PACKAGE__ auf das Paket bezieht, das die Rolle konsumiert, und nicht auf das "Sngleton" -Paket/die Rolle. Ich hatte gehofft, dass alle Variablen, die in einer Rolle definiert sind, auf das Paket beschränkt sind, das sie verbraucht, und nicht auf das Paket der Rolle. – mjn12

+1

Moose beeinflusst die Art und Weise, wie Perl Variablen analysiert oder versteht. Es gibt keinen (einfachen) Weg, um das Verhalten, das du hier anstellst, zu machen, und wäre sowieso völlig außerhalb des Rahmens von Elch. – perigrin

2

„Ich verstehe ein MooseX :: Singleton-Modul zur Verfügung, aber es gibt immer Widerstand, wenn andere CPAN-Modul für das Projekt erforderlich ist.“

Das ist wirklich etwas, das angegangen werden muss. Als Dep ist MX: Singleton sehr klein. Was ist das Problem? Sind Sie auf einem global freigegebenen Perl auf einem freigegebenen Server oder ähnlichem festgefahren? Wenn dies der Fall ist, sollten Sie sich local.lib genauer ansehen, um einzelnen Entwicklern zu ermöglichen, CPAN-Abhängigkeiten wie jedes andere CPAN-Modul mit einem Makefile.PL-Skript ordnungsgemäß zu verwalten.

+1

Vielen Dank, dass Sie sich die Zeit genommen haben, die Frage zu beantworten, aber das ist wirklich nicht hilfreich. Ich bat um Hilfe, um zu verstehen, warum dieser bestimmte Code nicht funktionierte UND erkannte die Existenz von MooseX :: singleton, um genau diese Art von Antwort zu vermeiden. – mjn12