在将对象推送到我的 perl Moo class 中的 ArrayOf[] 成员时,如何检查对象是否有效?

How do I check if an object is valid when pushed to an ArrayOf[] member in my perl Moo class?

我创建了一个包含对象数组的 Moo class,使用 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'] 检查对象,因为我实际上 再次设置数组引用

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

理想情况下,我什至不想知道 ArrayRef 中到底有什么,只知道它是必须检查的东西。我查看了 Type::Tiny 文档中的 _type_parameter_,但我不太清楚如何在我的代码中使用它。

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

Perl 本身是有效的 duck 类型,但 确实 支持 bless 将哈希引用加持到 class,允许您将方法附加到它。

就像在任何面向对象的系统中一样,classes 可以在 perl 中相互继承,你可以说任何 class isa 另一个class。然后你的代码可以简单地检查它是否已经传递了一个像 class.

这样幸运的哈希引用

因此,如果我正确理解您的问题,您可以通过确保您的 class 层次结构规定正确的方式来确保只将相关类型(适当祝福)的 classes 添加到您的数组中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 构建的大容量网络服务器中使用这种方法,对于其他面向对象环境的用户来说,它简单易懂。

这执行相同的检查:

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 可能是最好的选择。

未测试。

旧问题的答案,因为我已经有一段时间无法登录 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 会自动为您进行类型检查。