2009-08-07 2 views
11

Простой или лучше (= >) для использования Perl и Moose для создания классов на основе входящих данных?Как я могу закодировать завод в 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)[email protected]_; 
     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 

EDIT: Это просто поразило меня, что это довольно много, что происходит, когда вы звоните в DBI. В зависимости от параметров, которые Вы передаете, он будет использовать совершенно другой код при сохранении (в основном) последовательного интерфейса

ответ

10

Ick. У 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; 

Это примерно то, что другой плакат имел в виду об использовании на основе решения роли.

+0

спасибо, займет у меня некоторое время, чтобы заглянуть :-) – lexu

+1

** имеет [qw (SUBCLASS MSG)] => (is => 'ro', required => 1); ** аккуратный трюк .. но "неинтуитивный" для всех, кто не знаком с perl .. – lexu

+0

Нет, это не так. Он является частью API Moose и не имеет ничего общего с Perl. (Если вы хотите быть неинтуитивным, вы должны написать «имеет $ _ => (...) для qw/SUBCLASS MSG /». Но, конечно, все знают, что это делает.) – jrockway

5

Вы могли бы просто сделать:

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

Если это проще или лучше до вас, чтобы решить.

+0

, используя переменную как создаваемый класс ** $ case-> new (..) ** таким образом чувствует себя очень странно. Но да, чрезвычайно компактный, по сравнению с моим кодом (где я его прописал ** благословил ({}, $ case) ** Спасибо за отзыв! – lexu

+1

Почему это странно? Имя класса в Perl - это просто строка. Foo-> blah точно такая же, как «Foo» -> blah. – 2009-08-07 18:31:07

+1

Если на заводе нет логики, чтобы выбрать правильный подкласс для создания экземпляра, то это не большая часть фабрики, и это с большим размахом лучшее решение – Schwern

4

Ну, объект уже создан, когда BUILD называется, так что я бы сказал

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

Вы всегда хотите, чтобы перейти от модели, основанной на наследство на основе ролей модели, в которой вы создаете объект, который вы хотите (вместо того, чтобы передавать класс в заводский класс), затем примените общую роль.

+0

Где вы отправите меня, чтобы узнать/понять переход «наследования» на «ролевую основу»? – lexu

+3

Руководство Moose по ролям будет начато http://search.cpan.org/perldoc/Moose::Manual::Roles –

+0

@Sinan Ünür: спасибо за эту ссылку! – lexu

5

Только примечание на некоторые из ответов:

Вызов bless в BUILD, или где-нибудь за пределами СС внутренностей, всегда неприемлемо. (Если вы должны безвозвратно, есть 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»? Это похоже.)

3

Просто используйте другой заводский объект для создания объектов этого класса.

Simpler, более гибкий, более надежный и т.д.

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

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

где new_object может анализировать параметры и принимать решения по обоим данным внутри $factory и данных от этих параметров.

Когда вы выясните, что на следующем этапе вам понадобятся созависимые объекты, найдите инверсию структуры управления.