2010-06-17 4 views
5

Я пытаюсь написать одноэлементную роль с помощью Perl и Moose. Я понимаю, что модуль MooseX :: Singleton доступен, но всегда есть сопротивление, когда требуется другой модуль CPAN для нашего проекта. Попробовав это и немного потрудившись, я хотел бы понять, ПОЧЕМУ мой метод не работает. Одноточечно роль, которую я написал следующим образом:Singleton Роли в 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; 

Это, кажется, работает поиск, когда только один класс использует одноплодной роль. Однако, когда два класса (ClassA и ClassB, например) потребляют роль Singleton, они появляются, поскольку оба они ссылаются на общую переменную $ _singleInstance. Если я вызываю ClassA-> getInstance, он возвращает ссылку на объект ClassA. Если я вызову ClassB-> getInstance когда-нибудь позже в том же скрипте, он вернет ссылку на объект типа ClassA (хотя я явно назвал метод getInstance для ClassB). Если я не использую роль и на самом деле копирую и вставляю код из роли Singleton в ClassA и ClassB, он работает нормально. Что тут происходит?

+1

Вы понимаете, что упаковка «нового» просто требует мира обиды, не так ли? – Ether

ответ

3

Вы сохраняете экземпляр на всех типах, вместо использования другого для каждого типа класса.

Это требует фабричной шаблон проектирования, например:

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

Если вы действительно хотите одиночек, пожалуйста, установите MooseX :: Singleton, а не прокатке самостоятельно - если вы посмотрите на исходный код, вы увидите на него приходится много крайних случаев. Тем не менее, я бы посоветовал не заставлять ваши классы быть одиночными, так как это устраняет контроль над самим классом. Вместо этого используйте фабрику (как указано выше), поэтому вызывающий может решить, как построить класс, а не принуждать всех потребителей к одному пользователю.

1

Они делят переменную экземпляра. Вы должны выделить его внутри пакета, используя роль.

# 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

Если вы собираетесь идти по этому маршруту, использование роли метакласса, вероятно, намного более устойчиво, чем обезьяна с таблицей символов. – friedo

3

Ваш $_singleInstance лексически области видимости блока, где он появляется, в этом случае вся ваша Singleton пакет. Ваш модификатор around образует замыкание над этой переменной, то есть он видит тот же$_singleInstance каждый раз, когда он запускается, независимо от того, в какой класс он состоит.

Простой способ для решения этой проблемы было бы хранить одиночек в хэш:

my %_instances; 

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

Возможно, лучше всего было бы установить роль пользовательских метакласса, который хранит одноэлементный экземпляр для каждого класса, который потребляет эту роль.

+0

Я понял, что это проблема с переменной, которая привязана к роли.Это было немного запутанно, потому что __PACKAGE__, похоже, относится к пакету, который использует роль, а не к пакету/роли «Sngleton». Я надеялся, что любые переменные, определенные в роли, будут привязаны к пакету, потребляющему их, а не к пакету роли. – mjn12

+1

Moose влияет на то, как Perl анализирует или понимает переменные. Нет (простого) способа сделать поведение, которое вы здесь выполняете, и в любом случае полностью выходит за рамки Moose. – perigrin

2

«Я понимаю, модуль MooseX :: Singleton доступен, но всегда есть сопротивление, когда требует другого модуля CPAN для нашего проекта.»

Это действительно то, что нужно решать. Как dep, MX: Singleton очень мала. В чем проблема? Вы застряли на общедоступном Perl на общем сервере или аналогичном? Если это так, вы действительно должны посмотреть на local :: lib, который предназначен для того, чтобы отдельные разработчики могли правильно управлять зависимостями CPAN с помощью скрипта Makefile.PL, как и любой другой модуль CPAN.

+1

Спасибо, что нашли время, чтобы ответить на вопрос, однако это действительно не помогает. Я попросил помочь понять, почему этот код не работает И признал существование MooseX :: singleton, чтобы избежать такого ответа. – mjn12

Смежные вопросы