7

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;

これは、シングルトン ロールを使用するクラスが 1 つだけの場合に機能するようです。ただし、2 つのクラス (ClassA と ClassB など) の両方が Singleton ロールを使用すると、両方が共有 $_singleInstance 変数を参照しているように見えます。ClassA->getInstance を呼び出すと、ClassA オブジェクトへの参照が返されます。同じスクリプトで後で ClassB->getInstance を呼び出すと、タイプ ClassA のオブジェクトへの参照が返されます (明らかに ClassB の getInstance メソッドを呼び出したにもかかわらず)。ロールを使用せず、Singleton ロールのコードを実際にコピーして ClassA と ClassB に貼り付けると、正常に動作するように見えます。何が起きてる?

4

4 に答える 4

5

クラスタイプごとに異なるインスタンスを使用するのではなく、すべてのタイプでインスタンスを保存しています。

これには 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 をインストールしてください。ソースを見ると、それが多くのエッジ ケースを説明していることがわかります。ただし、クラス自体から制御が失われるため、クラスを強制的にシングルトンにしないことをお勧めします。代わりに、(上記のように) ファクトリを使用して、すべてのコンシューマーを 1 つのユースケースに強制するのではなく、呼び出し元がクラスの構築方法を決定できるようにします。

于 2010-06-17T19:48:51.053 に答える
4

Your$_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};
};

おそらくより良い方法は、そのロールを使用する各クラスのシングルトン インスタンスを格納するカスタム メタクラス ロールを設定することです。

于 2010-06-17T19:49:20.407 に答える
3

「MooseX::Singleton モジュールが利用可能であることは理解していますが、プロジェクトに別の CPAN モジュールが必要になると常に抵抗があります。」

これは本当に対処する必要があるものです。MX:Singleton は dep としては非常に小さいです。どうした?共有サーバーなどでグローバルに共有されている Perl で行き詰まっていますか? もしそうなら、あなたは本当に local::lib を見るべきです。これは、他の CPAN モジュールと同様に、個々の開発者が Makefile.PL スクリプトで CPAN の依存関係を適切に管理することを容易にするように設計されています。

于 2010-06-17T19:58:53.497 に答える
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
...
于 2010-06-17T19:48:55.790 に答える