代码之家  ›  专栏  ›  技术社区  ›  lexu

如何在Perl和Moose中编写工厂代码?

  •  11
  • lexu  · 技术社区  · 15 年前

    使用Perl和 Moose 根据输入的数据实例化类?

    下面的代码是我正在处理的项目中的一个简化示例。

    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时,几乎就是这样。根据您传递的参数,它将使用完全不同的代码,同时保持(大部分)一致的接口

    5 回复  |  直到 13 年前
        1
  •  10
  •   perigrin    13 年前

    艾克。Stevan有一个非常令人信服的论点 new 应该永远只 返回类的实例。其他任何事情都会让新来的人感到困惑 系统。

    你可能想看看 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
    

    当然,还有很多其他方法可以在驼鹿身上实现这一概念。不知道你的领域问题的具体情况,很难知道 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;
    

    这大致就是另一个海报关于使用基于角色的解决方案的意思。

        2
  •  5
  •   innaM    15 年前

    你可以简单地做:

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

    如果这更容易或更好,由你来决定。

        3
  •  5
  •   jrockway    15 年前

    只是一些答案的注释:

    打电话 bless 内部或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!' );
    

    最后,如果无法创建消息实例,那么它不应该是类。它应该是一个角色。

    当然,您可以使用其他对象处理建筑。只需确保另一个对象只负责理解字符串格式,而不是消息内部:

    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”?这是相似的。)

        4
  •  4
  •   Chas. Owens    15 年前

    好吧,当 BUILD 我要说的是

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

    您可能总是希望从基于继承的模型切换到基于角色的模型,在该模型中创建所需的对象(而不是将类传递到工厂类中),然后应用公共角色。

        5
  •  3
  •   nothingmuch    15 年前

    只需使用另一个工厂对象来构造该类的对象。

    更简单、更灵活、更可靠等。

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

    my$object=$factory->新建对象(…各种参数…);

    哪里 new_object 可以解析参数并对内部的两个数据进行决策 $factory 以及这些参数的数据。

    当您发现在下一步中需要相互依赖的对象时,请寻找控制框架的反转。