Роли Singleton в американском лосе

Я пытаюсь записать одноэлементную роль с помощью Perl и Американского лося. Я понимаю MooseX:: модуль Singleton доступен, но всегда существует сопротивление при требовании другого модуля CPAN для нашего проекта. После попытки этого и испытывания небольших затруднений я хотел бы понять, ПОЧЕМУ мой метод не работает. Одноэлементная роль, которую я записал, следующие:

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, это, кажется, хорошо работает. Что продолжается здесь?

6
задан mjn12 17 June 2010 в 19:10
поделиться

4 ответа

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

# 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
...
1
ответ дан 9 December 2019 в 22:29
поделиться

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

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

2
ответ дан 9 December 2019 в 22:29
поделиться

Ваш $_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};
};

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

4
ответ дан 9 December 2019 в 22:29
поделиться

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

Это требует паттерна проектирования Factory, например:

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, а не создавайте свой собственный - если вы посмотрите на исходный текст, то увидите, что он учитывает множество крайних случаев. Однако я бы не советовал заставлять ваши классы быть синглтонами, так как это лишает класс контроля. Вместо этого используйте фабрику (как описано выше), чтобы вызывающая сторона могла решать, как построить класс, а не заставлять всех потребителей использовать его в одном сценарии.

4
ответ дан 9 December 2019 в 22:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: