我正在使用HTML::FormHandler。要使用它,我们应该从中继承它,然后您可以覆盖某些属性,例如field_name_space
或attribute_name_space
。
但是,我现在有很多表单都扩展HTML::FormHandler
或其基于DBIC的变体HTML::FormHandler::Model::DBIC,因此重复多次这些重叠属性。
我尝试将它们置于某个角色中,但却收到错误,即角色不支持+attr
表示法。很公平。
消除这种重复的最佳方法是什么?我想也许是继承,但是我必须为HTML::FormHandler
和HTML::FormHandler::Model::DBIC
做两次,而且我相信一般的想法是使用Roles通常可以更好地实现子类化。
更新:我认为举个例子是个好主意。这就是我目前正在做的 - 它涉及代码重复。正如您所看到的,一个表单使用不同的父类,因此我无法创建一个父类来放置属性覆盖。我必须创建两个 - 这也会增加冗余。
package MyApp::Form::Foo;
# this form does not interface with DBIC
extends 'HTML::Formhandler';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
package MyApp::Form::Bar;
# this form uses a DBIC object
extends 'HTML::Formhandler::Model::DBIC';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
package MyApp::Form::Baz;
# this form also uses a DBIC object
extends 'HTML::Formhandler::Model::DBIC';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
答案 0 :(得分:4)
首先,角色组成一个类,它们与子类化无关。子类是一个完整的类,它扩展了父类(或多个,但根据我的经验,如果可以的话,应该避免多重继承)。角色是一种行为,或者可以应用于类的parial接口。然后角色直接修改类。一般来说,没有新的课程。
因此,继承和角色组合实际上是两种不同的东西和两种不同的设计。因此,你不能简单地为另一个交换一个。两者都有不同的设计含义。
我使用HTML::FormHandler
的策略是为我需要的每个表单创建一个真正的子类,并将我想要重用的表单的不同行为放入角色中。
我认为这个问题(如何以干净和理智的方式实现所需的扩展)如果不了解您的目标实际设计,就无法真正回答。
更新:我明白你的意思,这是一个棘手的案例。 HTML::FormHandler
主要通过继承来扩展。所以我认为最好的策略确实是有两个子类,一个用于HTML::FormHandler
,另一个用于HTML::FormHandler::Model::DBIC
。一开始看起来很乏味,但从长远来看,你可能想要为它们设置不同的设置。为避免重复实际配置(默认值),我将尝试以下(此示例为普通HFH,不带DBIC):
package MyApp::Form;
use Moose;
use namespace::autoclean;
extends 'HTML::FormHandler';
with 'MyApp::Form::DefaultSettings';
# only using two fields as example
for my $field (qw( html_prefix field_traits )) {
has "+$field", default => sub {
my $self = shift;
my $init = "_get_default_$field";
my $method = $self->can($init)
or die sprintf q{Class %s does not implement %s method}, ref($self), $init;
return $self->$method;
};
}
1;
请注意,如果需要另一个属性的值进行计算,则需要使属性变为惰性。上面的基类会查找钩子来查找初始值。您必须在两个类中执行此操作,但您可以将默认子例程生成放入从库中导入的函数中。由于以上不再需要直接操作属性来更改默认值,因此您可以将这些内容放在我上面称为MyApp::Form::DefaultSettings
的角色中:
package MyApp::Form::DefaultSettings;
use Moose::Role;
use namespace::autoclean;
sub _build_html_prefix { 1 }
sub _build_field_traits { ['MyApp::Form::Trait::Field'] }
1;
此方法将允许您的角色影响默认值构造。例如,您可以根据上面的一个角色使用around
修改值。
还有一个非常简单,但在我看来有点丑陋的方式:你可以让角色提供一个改变价值的BUILD
方法。这一开始看起来非常简单直接,但它具有简单易用性和灵活性。它工作简单,但也只适用于非常简单的情况。由于Web应用程序中的表单数量通常很高,而且需求可能非常多样化,因此我建议使用更灵活的解决方案。
答案 1 :(得分:1)
HTML::FormHandler::Model::DBIC
的代码实际上是Moose特征,以帮助解决这种情况。您可以从基类继承,并且在使用DBIC模型的表单中,您可以执行
with 'HTML::FormHandler::TraitFor::Model::DBIC';
答案 2 :(得分:0)
这个方法,使用多重继承(我知道,我知道,呃),在一个类中放置常见的默认覆盖,然后在其他类中放置自定义代码?
package MyApp::Form;
use Moose;
extends 'HTML::Formhandler';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
package MyApp::Form::Model::DBIC;
use Moose;
extends 'MyApp::Form', 'HTML::Formhandler::Model::DBIC';
# ... your DBIC-specific code
现在您可以根据需要从MyApp :: Form或MyApp :: Form :: Model :: DBIC下载。