2

我正在使用HTML::FormHandler。要使用它,应该从它继承子类,然后您可以覆盖一些属性,例如field_name_spaceor attribute_name_space

但是,我现在有很多表单都在扩展HTML::FormHandler或其基于 DBIC 的变体HTML::FormHandler::Model::DBIC,因此这些被覆盖的属性重复了很多次。

我试图将它们放在一个角色中,但得到一个错误,即+attr角色中不支持符号。很公平。

消除这种重复的最佳方法是什么?我想也许是子类化,但后来我不得不为HTML::FormHandlerand做两次,而且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');

...
4

3 回答 3

4

首先,角色被组合成一个类,它们与子类化无关。子类是一个完整的类,它扩展了一个父类(或多个,但根据我的经验,如果可以的话,应该避免多重继承)。角色是一种行为,或可应用于类的部分接口。然后角色直接修改类。一般没有创建新的类。

所以继承和角色组合实际上是两种不同的东西和两种不同的设计。因此,您不能简单地将一个换成另一个。两者都有不同的设计含义。

我的策略HTML::FormHandler是为我需要的每个表单创建一个真正的子类,并将我想要重用的表单的不同行为放入角色中。

我认为如果不知道您的实际设计目标,就无法真正回答这个问题(如何以干净和理智的方式实现您需要的扩展)。

更新:我明白你的意思,这是一个棘手的案例。HTML::FormHandler主要针对通过继承进行扩展。所以我认为最好的策略确实是有两个子类,一个 forHTML::FormHandler和一个 for 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 应用程序中的表单数量通常相当多,而且需求可能非常多样化,因此我建议使用更灵活的解决方案。

于 2010-08-03T15:00:37.060 回答
1

的代码HTML::FormHandler::Model::DBIC实际上是 Moose 特征,以帮助解决这种情况。您可以从基类继承,并且在使用 DBIC 模型的表单中,您可以执行

with 'HTML::FormHandler::TraitFor::Model::DBIC';
于 2013-02-05T17:50:08.477 回答
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 下降。

于 2010-08-03T16:41:18.973 回答