2

我创建了一个 Moo 类,其中包含一个对象数组,该数组使用Types::Standard对照一个类型进行检查,并且我想验证我是否将正确的对象放入该数组中,而无需进行过多的硬编码。这是我现在拥有的示例:

package MyClass;
use Moo;
use Types::Standard qw( ArrayRef InstanceOf );

# Initially empty array of objects
has connected => (
    is => 'ro',
    isa => ArrayRef[InstanceOf['MyClass']],
    default => sub { [] }
);

# Add an object to our internal list
sub connect {
    my ( $self, $other ) = @_;
    # TODO: Check if "$other" is actually an InstanceOf['MyClass']
    # without doing "$self->connected( $self->connected )"
    push @{$self->connected}, $other;
}

connect()中,我将对象添加到我的内部数组中,但如果我理解正确,则永远不会检查该对象,InstanceOf['MyClass']因为我实际上并没有再次设置数组引用。

对每个新对象执行此验证的好方法是什么?在推送了一个新项目后,我曾短暂地考虑过这样做$self->connected( $self->connected ),但这必须验证每个对象。

理想情况下,我什至不想确切地知道 ArrayRef 中的内容,只是需要检查它。我查看了Type::Tiny文档中的 _type_parameter_ ,但我不太清楚如何在我的代码中使用它。

4

3 回答 3

3

一个老问题的答案,因为我已经有一段时间无法登录 StackExchange...

package MyClass;
use Moo;
use Types::Standard qw( ArrayRef InstanceOf );
use Sub::HandlesVia;

has connected => (
    is => 'ro',
    isa => ArrayRef[InstanceOf['MyClass']],
    default => sub { [] },
    handles_via => 'Array',
    handles => {
      'connect' => 'push',
    }
);

Sub::HandlesVia 将自动为您处理类型检查。

于 2020-08-31T12:09:17.280 回答
2

这个答案忽略了 Moo,因为你可以在不使用它的情况下实现你所要求的。如果您愿意,这不会阻止您使用它,但不需要按照您的要求进行操作。

Perl 本身是有效的鸭子类型,但确实支持 bless,它将哈希引用祝福到一个类中,允许您将方法附加到它。

就像在任何面向对象系统中的类可以在 perl 中相互继承一样,您可以说任何类都是另一个类。然后,您的代码可以简单地检查它是否已通过作为该类祝福的哈希引用。

因此,如果我正确理解您的问题,您可以通过确保您的类层次结构规定正确的 isa 关系并检查它来确保只将相关类型的类(适当地祝福)添加到您的数组中。

因此,如果您定义一个基类型,即您要检查的类型,您可以让所有其他类型声明它们是:

您的基础中可能的示例声明:

package MyCore;

sub new
{
my ($Class, $obj);

  $Class=shift;
  $obj= { Key => 'Value' };
  bless($obj, $Class);
  return($obj);
}

派生类型中的可能声明:

use MyCore;

package MyDerived;
@MyDerived::ISA=('MyCore');

sub new()
{
my ($Class, $Obj);

  $Class=shift;
  $Obj=MyCore::new();
  $Obj->{NewKey}='NewValue';
  bless($Obj, $Class);
}

然后,当您添加到数组时:

sub connect()
{
  my ($Self, $Other);
  $Self=shift;
  $Other=($#_>=0)?shift:undef;

  if(defined($Other) && $Other->isa('MyCore'))
  {
    push(@{$Self->{Connected}}, $Other);
  }
}

我们在基于 mod_perl 的大容量网络服务器中使用这种方法,并且对于其他面向对象环境的用户来说,它简单易行。

于 2019-08-06T22:05:52.580 回答
2

这执行相同的检查:

use Type::Tiny::Class qw( );

my $MyClass_type = Type::Tiny::Class->new( class => 'MyClass' );

sub connect {
    my ( $self, $other ) = @_;

    $MyClass_type->check($other)
       or die "Validation error";

    push @{ $self->connected }, $other;
}

看起来你也可以使用

if ( my $error = $MyClass_type->validate($other, '$other') ) {
   die($error);
}

if ( my $errors = $MyClass_type->validate_explain($other, '$other') ) {
   die(join("\n", @$errors));
}

->validate可能是最好的选择。

未经测试。

于 2019-08-06T23:13:45.473 回答