Синглтон Роли в Музе - PullRequest
       3

Синглтон Роли в Музе

6 голосов
/ 17 июня 2010

Я пытаюсь написать одиночную роль, используя Perl и Moose. Я понимаю, что модуль 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;

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

Ответы [ 4 ]

4 голосов
/ 17 июня 2010

Ваш $_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 голосов
/ 17 июня 2010

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

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

2 голосов
/ 17 июня 2010

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

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

1 голос
/ 17 июня 2010

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

# 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
...
...