2010-06-17 6 views
5

Próbuję napisać rolę singleton za pomocą Perl i Moose. Rozumiem, że moduł MooseX :: Singleton jest dostępny, ale zawsze jest wymagany opór, gdy wymagany jest inny moduł CPAN dla naszego projektu. Po wypróbowaniu tego i kłopotach chciałbym zrozumieć, DLACZEGO moja metoda nie działa. Rola Singleton Pisałem jest następujący:Role Singleton w 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; 

To wydaje się działać znaleźć, gdy tylko jedna klasa używa singleton rolę. Jednakże, gdy dwie klasy (na przykład ClassA i ClassB) obaj przyjmują rolę Singleton, wydaje się, że odnoszą się one do wspólnej zmiennej $ _singleInstance. Jeśli wywołasz ClassA-> getInstance, zwróci referencję do obiektu ClassA. Jeśli wywołaję ClassB-> getInstance później w tym samym skrypcie, zwróci to odwołanie do obiektu typu ClassA (mimo że wyraźnie nazwałem metodę getInstance dla ClassB). Jeśli nie użyję roli, a właściwie skopiuję i wkleję kod z roli Singleton do ClassA i ClassB, to wygląda na to, że działa dobrze. Co tu się dzieje?

+1

Zdajesz sobie sprawę, że zawijanie słowa "nowy" jest po prostu pytaniem o świat bólu, prawda? – Ether

Odpowiedz

3

Zapisujesz instancję we wszystkich typach, zamiast używać innej dla każdego typu klasy.

Wymaga projektowania wzorca Factory np

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

Jeśli naprawdę chcesz singletons, zainstaluj MooseX :: Singleton zamiast toczenia własne - jeśli spojrzeć na źródła zobaczysz odpowiada za wiele przypadków skrajnych. Jednak odradzam zmuszanie twoich klas do bycia singletonami, ponieważ to usuwa kontrolę z samej klasy. Zamiast tego użyj fabryki (jak wyżej), aby dzwoniący mógł zdecydować, jak skonstruować klasę, zamiast zmuszać wszystkich użytkowników do jednego zastosowania.

1

Udostępniają zmienną instancji. Musisz przydzielić go wewnątrz paczki za pomocą roli.

# 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

Jeśli zamierzasz iść tą drogą, użycie roli metaklasy jest prawdopodobnie znacznie bardziej niezawodne niż małpowanie z tabelą symboli. – friedo

3

Twój $_singleInstance jest leksykalnie zawężona do bloku, w którym się pojawia, w tym wypadku cała Singleton pakietu. Twój modyfikator around tworzy zamknięcie tej zmiennej, co oznacza, że ​​widzi ona zawsze za każdym razem, gdy jest uruchamiana, niezależnie od klasy, w którą się skomponuje.

Prostym sposobem na rozwiązanie to byłoby do przechowywania pojedynczych w hash:

my %_instances; 

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

Być może lepszym sposobem byłoby założyć rolę zwyczaj metaklasa który przechowuje singleton instancji dla każdej klasy, który zużywa ta rola.

+0

Pomyślałem, że problem polegał na tym, że zmienna ma zasięg do tej roli.Było to trochę mylące, ponieważ __PACKAGE__ wydaje się odnosić do pakietu, który zajmuje tę rolę, a nie do pakietu/roli "Sngleton". Miałem nadzieję, że dowolne zmienne zdefiniowane w roli będą miały zasięg do pakietu, który je pochłonie, a nie do pakietu roli. – mjn12

+1

Łoś wpływa na sposób, w jaki Perl analizuje lub rozumie zmienne. Nie ma (łatwego) sposobu, aby twoje zachowanie przyszło tutaj działać, i tak czy tak byłby całkowicie poza zasięgiem łosia. – perigrin

2

„Rozumiem moduł MooseX :: Singleton jest dostępny, ale nie zawsze jest opór podczas wymagając innego modułu CPAN dla naszego projektu.”

To jest naprawdę coś, że należy się zająć. Jako dep, MX: Singleton jest bardzo mały. Jaki jest problem? Czy utknąłeś na udostępnionym na całym świecie Perlu na wspólnym serwerze lub podobnym? Jeśli tak, powinieneś spojrzeć na local :: lib, który ma na celu ułatwienie poszczególnym programistom właściwego zarządzania zależnościami CPAN za pomocą skryptu Makefile.PL, tak jak w przypadku każdego innego modułu CPAN.

+1

Dziękujemy za poświęcenie czasu, aby odpowiedzieć na pytanie, ale to naprawdę nie jest pomocne. Poprosiłem o pomoc w zrozumieniu, dlaczego ten konkretny kod nie działa i potwierdziłem istnienie MooseX :: singleton, aby uniknąć właśnie tego rodzaju reakcji. – mjn12