如何使用 Perl 和 Moose 编写工厂代码?
是否有更简单或更好(=>更易于维护)的方法来使用 Perl 和 Moose
< /a> 根据传入数据实例化类?
以下代码是我正在处理的项目中的精简示例。
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 时发生的情况。 根据您传递的参数,它将使用完全不同的代码,同时保持(大部分)一致的接口
Is there a simpler or better (=>easier to maintain) way to use Perl and Moose
to instantiate classes based on incoming data?
The following code is a stripped down sample from a project I'm working on.
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
EDIT: It just struck me that this is pretty much what happens when you call the DBI. Depending on the parameters you pass, it will use entirely different code while maintaining a (mostly) consistent interface
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
恶心。 Stevan 有一个非常令人信服的论点,即
new
应该始终只返回类的实例。 其他任何事情都会让新人感到困惑
系统。
你可能想看一下
MooseX::AbstractFactory。
如果这对您不起作用:
当然,还有许多其他方法可以在 Moose 中实现相同的概念。 如果不知道您的域问题的具体情况,很难判断 MooseX::Traits 不会更好:
这大致就是另一张海报关于使用基于角色的解决方案的意思。
Ick. Stevan has a very compelling argument that
new
should always onlyreturn an instance of Class. Anything else is confusing to new people learning
the system.
You might wanna take a look at
MooseX::AbstractFactory.
If that won't work for you then:
Of course there are many other ways to implement this same concept in Moose. Without knowing the specifics of your domain problem it's hard to tell that something like MooseX::Traits wouldn't be better:
This is roughly what the other poster meant about using a Role based solution.
您可以简单地这样做:
是否更容易或更好由您决定。
You could simply do:
If that is easier or better is up to you to decide.
只是对一些答案的注释:
在 BUILD 中或 MOP 内部之外的任何地方调用
bless
总是不可接受的。 (如果你一定要反驳,有Class::MOP::Class->rebless_instance
!)我同意不允许
new
返回实例以外的任何内容的建议__PACKAGE__
。 如果您想要一个创建某物实例的方法,请将其命名为其他名称。 示例:然后,当您想要创建文字消息时:
当您想要解析字符串并返回正确的消息子类时:
最后,如果创建 Message 实例没有意义,那么它不应该成为一个班级。 应该是一个角色。
当然,您可以使用其他对象来构建。 例如,只需确保另一个对象仅负责理解字符串格式,而不是消息内部结构:
现在您不再关心让某些“超类”负责构建“子类”,我认为这是更好的设计。 (记住,MessageString 对执行“Message”的类没有特殊的权力。这是这里的关键;它只负责理解字符串化消息。)
无论如何,现在您只需:(
您知道“MVC”吗?这很相似。 )
Just a note on some of the answers:
Calling
bless
in BUILD, or anywhere outside of the MOP internals, is always unacceptable. (If you must rebless, there isClass::MOP::Class->rebless_instance
!)I second the advice on not allowing
new
to return anything other than an instance of__PACKAGE__
. If you want a method that creates an instance of something, call it something else. Example:Then, when you want to create a literal message:
When you want to parse a string and return the correct message subclass:
Finally, if it doesn't make sense to be able to create an instance of Message, then it should not be a class. It should be a role.
You can handle building with some other object, of course. Just make sure this other object is responsible only for understanding the string format, for example, and not message internals:
Now you are no longer concerned with having some "superclass" responsible for building "subclasses", which I think is better design. (Remember, MessageString has no special power over the classes that do "Message". That is the key here; it is only responsible for understanding stringified messages.)
Anyway, now you just:
(You know "MVC"? This is similar.)
只需使用另一个工厂对象来构造该类的对象即可。
更简单、更灵活、更可靠等
my $factory = Factory->new( ... 工厂参数 ... );
my $object = $factory->new_object( ... 各种参数 ... );
其中
new_object
可以解析参数并根据$factory
内部的数据和这些参数中的数据做出决策。当您发现下一步需要相互依赖的对象时,请寻找控制反转框架。
Just use another factory object to construct objects of that class.
Simpler, more flexible, more reliable, etc.
my $factory = Factory->new( ... factory parameters ... );
my $object = $factory->new_object( ... various parameters ... );
where
new_object
can parse the parameters and make decisions on both data inside$factory
and data from those parameters.When you figure out that you'll need codependent objects in the next step, look for an inversion of control framework.
好吧,当调用
BUILD
时,该对象已经创建了,所以我想说,您可能总是希望从基于继承的模型切换到基于角色的模型,在该模型中创建所需的对象(而不是传递类进入工厂类),然后应用公共角色。
Well, the object is already created when
BUILD
is called, so I would sayYou may always wish to switch from an inheritance based model to a role based model where you create the object you want (rather than passing the class into the factory class), then apply the common role.