Использование замыканий для изменения классов внутри блоков BEGIN Perl

ПРЕДИСЛОВИЕ:Пожалуйста, ради этого обсуждения, давайте на мгновение проигнорируем тот факт, что та же цель может быть достигнута с помощью Class::Accessor или даже просто с помощью Moose (, вероятно, с лучшими результатами при учете удобочитаемости и ремонтопригодности кода).

Что касается объектно-ориентированного Perl, в книге Programming Perlобсуждается возможность создания методов доступа с замыканиями. Например, это допустимый фрагмент кода:

#!perl

use v5.12;
use warnings;

# at run-time
package Person1;

my @attributes = qw/name age address/;

for my $att ( @attributes )
{
  my $accessor = __PACKAGE__. "::$att";

  no strict 'refs'; # allow symbolic refs to typeglob

  *$accessor = sub {
    my $self = shift;
    $self->{$att} = shift if @_;
    return $self->{$att};
  };
}

sub new { bless {}, shift }

package main;

use Data::Dumper;

my $dude = Person1->new;
$dude->name('Lebowski');
say Dumper($dude);

В приведенном выше примере, если я не ошибаюсь, класс составляется во время выполнения-, а его методы доступа создаются одновременно с созданием экземпляра класса.. Это означает, что при создании объекта будет штраф за скорость.

Теперь рассмотрим следующую альтернативу:

#!perl

use v5.12;
use warnings;

package Person2;

BEGIN
{
  for my $att (qw/name age address/)
  {
    my $accessor = __PACKAGE__. "::$att";

    no strict 'refs'; # allow symbolic refs to typeglob

    *$accessor = sub {
      my $self = shift;
      $self->{$att} = shift if @_;
      return $self->{$att};
    };
  }
}

sub new { bless {}, shift }

package main;

use Data::Dumper;

my $dude = Person2->new;
$dude->name('Lebowski');
say Dumper($dude);

В этой версии композиция создается внутри BEGINблока (, т. е. во время компиляции-), и я полагаю, что, решая эту задачу как как можно быстрее в жизненном-цикле программы, я экономлю время при создании объекта во время выполнения-.

Простое Benchmark,

# benchmark it!
package main;

use Benchmark qw/cmpthese/;

cmpthese(-2, {
  accessors_new   => sub { Person1->new },
  accessors_begin => sub { Person2->new },
});

по-видимому, подтверждает мою теорию этими результатами:

                    Rate accessors_begin   accessors_new
accessors_begin 853234/s              --             -9%
accessors_new   937924/s             10%              --

Предполагая, что мои рассуждения до сих пор были правильными,

  • Какие другие преимущества/недостатки существуют при сравнении обеих этих стратегий?
  • Стоит ли полагаться на блоки BEGINкак на эффективный способ манипулирования классами такого рода?
  • Когда это не рекомендуется?

7
задан Sérgio Bernardino 7 March 2012 в 12:30
поделиться