12

MoosePerlを使用し、受信データに基づいてクラスをインスタンス化するためのより簡単な、またはより良い(=>保守が容易な)方法はありますか?

次のコードは、私が取り組んでいるプロジェクトから抜粋したサンプルです。

package FooBar;
use Moose;
has 'SUBCLASS' =>('isa'=>'Str',required=>'1',is=>'ro');
has 'MSG' =>('isa'=>'Str',required=>'1',is=>'ro');

sub BUILD {
      my $self = shift;
      my ($a)=@_;
      bless($self,$a->{SUBCLASS})
}
sub Hi {
   my $self=shift;
   print "Hi, I'm a " . ref($self)  ." and I say [". $self->MSG()."]\n";
}

package Foo;
use Moose;
extends ("FooBar");

package Bar;
use Moose;
extends ("FooBar");

package main;
use strict;
use warnings;

for my $line (<DATA>) {
   my ($case,$msg)=split(/[\n\r,]\s*/,$line);
   FooBar->new(SUBCLASS=>$case,MSG=>$msg)->Hi();
}

__DATA__
Foo, First Case
Bar, Second Case

編集:これは、DBIを呼び出すときに発生することとほぼ同じであることに気づきました。渡すパラメータに応じて、(ほとんど)一貫性のあるインターフェイスを維持しながら、まったく異なるコードを使用します

4

5 に答える 5

11

いや。Stevanには非常に説得力のある議論がnewあり、常にClassのインスタンスのみを返す必要があります。他の何かは、システムを学ぶ新しい人々を混乱させます。

MooseX::AbstractFactoryを見てみたいと思うかもしれません 。それがうまくいかない場合は、次のようにします。

package FooBar;
use Moose;

has [qw(SUBCLASS MSG)] => ( is => 'ro', required => 1);

sub create_instance {
    return $self->package->new(message => $self->msg);
}

package FooBar::Object;
use Moose;

has msg => ( is => 'ro', required => 1);

sub Hi {
   my $self = shift;
   print "Hi, I'm a " . ref($self)  ." and I say [". $self->MSG()."]\n";
}

package Foo;
use Moose;
extends qw(FooBar::Object);

package Bar;
use Moose;
extends qw(FooBar::Object);


package main;
or my $line (<DATA>) {
   my ($case,$msg)=split(/[\n\r,]\s*/,$line);
   FooBar->new(SUBCLASS=>$case,MSG=>$msg)->create_instance->Hi
}

__DATA__
Foo, First Case
Bar, Second Case

もちろん、これと同じ概念をMooseに実装する方法は他にもたくさんあります。ドメインの問題の詳細を知らなければ、MooseX ::Traitsのようなものが良くないだろうと言うのは難しいです:

package Foo;
use Moose;
with qw(MooseX::Traits);

package Bar;
use Moose;
with qw(MooseX::Traits);

package Messaging;
use Moose::Role;

has msg => ( is => 'ro', required => 1);

sub Hi {
   my $self = shift;
   print "Hi, I'm a " . ref($self)  ." and I say [". $self->MSG()."]\n";
}

package main;
use strict;
Foo->with_traits('Messaging')->new( msg => 'First Case')->Hi;

これは、他の投稿者が役割ベースのソリューションを使用することについて大まかに意味したことです。

于 2009-08-07T14:32:32.843 に答える
6

あなたは簡単に行うことができます:

$case->new( MSG => $msg )->Hi();

それが簡単か良いかはあなた次第です。

于 2009-08-07T12:12:49.360 に答える
5

いくつかの答えについてのメモ:

blessBUILD、またはMOP内部の外部での呼び出しは、常に受け入れられません。(あなたが祝福しなければならないなら、ありますClass::MOP::Class->rebless_instance!)

new次に、のインスタンス以外のものを返さないようにすることについてのアドバイスを示します__PACKAGE__。何かのインスタンスを作成するメソッドが必要な場合は、それを別の名前で呼び出します。例:

class Message {
   method new_from_string(Str $msg){
       my ($foo, $bar, $baz) = ($msg =~ /<...>/); # blah blah blah
       my $class = "Message::${foo}::$baz";
       Class::MOP::load_class($class);
       return $class->new( bar => $msg );
   }
}

次に、リテラルメッセージを作成する場合:

Message->new( whatever => 'you want' );

文字列を解析して正しいメッセージサブクラスを返したい場合:

Message->new_from_string( 'OH::Hello!' );

最後に、Messageのインスタンスを作成できることが意味をなさない場合は、クラスであってはなりません。それは役割でなければなりません。

もちろん、他のオブジェクトを使用してビルドを処理することもできます。たとえば、この他のオブジェクトが文字列形式の理解のみに関与し、メッセージの内部には関与しないことを確認してください。

class MessageString {
    has 'string' => ( initarg => 'string', reader => 'message_as_string' );

    method new_from_string(ClassName $class: Str $string) {
        return $class->new( string => $string );
    }

    method as_message_object {
        # <parse>
        return Message::Type->new( params => 'go here', ... );
    }
}

role Message { ... }
class Message::Type with Message { ... }

これで、「サブクラス」の構築を担当する「スーパークラス」が存在することに関心がなくなりました。これは、より優れた設計だと思います。(MessageStringは、「Message」を実行するクラスに対して特別な権限を持っていないことを忘れないでください。これがここでの鍵です。文字列化されたメッセージの理解にのみ責任があります。)

とにかく、今あなたはただ:

my $data =  <>; # Yup, I called it $data.  Sorry, Andy Lester.
my $parsed = MessageString->new_from_string( $data );
my $message = $parsed->as_message_object;
$message->interact_with

(「MVC」をご存知ですか?これは似ています。)

于 2009-08-07T18:02:22.067 に答える
4

別のファクトリオブジェクトを使用して、そのクラスのオブジェクトを作成するだけです。

よりシンプルで、より柔軟で、より信頼性が高いなど。

my $factory = Factory->new( ... factory parameters ... );

my $object = $factory->new_object( ... various parameters ... );

ここで、パラメーターを解析し、内部のデータとそれらのパラメーターからのデータnew_objectの両方について決定を下すことができます。$factory

次のステップで相互依存オブジェクトが必要になることがわかったら、制御の反転を探します。

于 2009-08-07T18:19:06.573 に答える
3

さて、オブジェクトはBUILD呼び出されたときにすでに作成されているので、私は言うでしょう

sub BUILD {
      my $self = shift;
      return bless $self, $self->SUBCLASS;
}

継承ベースのモデルから、(クラスをファクトリクラスに渡すのではなく)必要なオブジェクトを作成するロールベースのモデルに切り替えてから、共通のロールを適用することをお勧めします。

于 2009-08-07T13:08:12.607 に答える