Как переклассифицировать объект Perl - PullRequest
4 голосов
/ 29 октября 2010

Я работаю с несколькими пакетами Perl, мы будем называть их Some::Parser и Some::Data. У объекта Some::Parser есть методы для возврата объектов типа Some::Data. Я написал класс, который расширяет класс Some::Data, назовем его My::Data. Объекты класса My::Data на самом деле являются просто объектами класса Some::Data, но с дополнительными методами, с которыми легче работать.

Моя проблема в том, что я хочу продолжать использовать класс Some::Parser для тяжелой работы по анализу данных. Как я уже говорил ранее, Some::Parser объекты дают мне Some::Data объекты. Если у меня есть объект Some::Data, есть ли способ реклассифицировать его как объект My::Data? Как бы я это сделал?

Я полностью готов изменить свой подход, если предположить, что кто-то может предложить лучший способ сделать то, что я хочу, но написание собственного парсера - это не то, чем я заинтересован!

Ответы [ 5 ]

10 голосов
/ 29 октября 2010

Это пахнет как кусочек. Возможно, пришло время переосмыслить вашу стратегию. Например, может быть, вы должны написать My::Parser, который возвращает My::Data объектов.

Но если вы не хотите этого делать, вы можете вручную использовать bless для изменения класса объекта:

my $obj = Some::Data->new;
bless $obj, 'My::Data';

См. благослови в perldoc.

7 голосов
/ 29 октября 2010

Вероятно, лучший способ обработать что-то подобное - для Some::Parser предоставить способ указать класс, который он должен использовать для объектов данных.Например, HTML :: TreeBuilder предоставляет метод element_class.Если вы хотите, чтобы TreeBuilder создавал что-то отличное от HTML::Element узлов, вы создаете подкласс HTML::TreeBuilder и переопределяете element_class, чтобы вернуть нужный класс узлов.(Фактический код в TreeBuilder немного сложнее, потому что до HTML-Tree 4 был другой механизм для этого, и новый сопровождающий не хотел его нарушать.)

Я так понимаючто вы не написали Some::Parser, но, возможно, он уже имеет эту возможность.Если нет, возможно, его сопровождающий примет патч.Это должно быть довольно простое изменение.Вы просто добавили бы метод data_class (sub data_class { 'Some::Data' }), а затем изменили Some::Data->new на $self->data_class->new.Затем вы можете создать подкласс Some::Parser для создания My::Parser и просто переопределить data_class.

5 голосов
/ 29 октября 2010

Вы можете повторно bless что угодно.

Наследование в Perl 5 - это не что иное, как поиск @ISA.

3 голосов
/ 29 октября 2010

Вы можете повторно благословить возвращенный объект на все, что душе угодно:

#!/usr/bin/perl

package Some::Data;
use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub a { $_[0]->{a} }

package My::Data;
use strict; use warnings;
use base 'Some::Data';

sub a_squared {
    my $self = shift;
    my $v = $self->a;
    return $v * $v;
}

package Some::Parser;
use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub parse { return Some::Data->new(a => 3) }

package main;

use strict; use warnings;

my $data = Some::Parser->new->parse;
bless $data => 'My::Data';

printf "%.1f\t%.1f\n", $data->a, $data->a_squared;

В качестве альтернативы, вы можете использовать идею @ cjm:

#!/usr/bin/perl

package Some::Data;
use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub a { $_[0]->{a} }

package My::Data;
use strict; use warnings;
use base 'Some::Data';

sub a_squared {
    my $self = shift;
    my $v = $self->a;
    return $v * $v;
}

package Some::Parser;
use strict; use warnings;

sub new { my $class = shift; bless { @_ } => $class }

sub parse {
    my $self = shift;
    return $self->data_class->new(a => 3);
}

sub data_class { $_[0]->{data_class} }

package main;

use strict; use warnings;

my $data = Some::Parser->new(data_class => 'My::Data')->parse;
printf "%.1f\t%.1f\n", $data->a, $data->a_squared;
1 голос
/ 11 июня 2012

Я бы подумал о том, чтобы снова получить благословение. Когда у вас есть объект, вы не можете точно сказать, был ли он создан с использованием его конструктора (обычно Foo :: new ()), или кто-то благословил какой-то другой объект.

Проблема в том, что некоторые конструкторы толстые, это означает, что они делают намного больше, чем просто благословляют что-то:

sub new {
    my $pkg = shift;
    my ($required) = @_;
    croak "Bad call" unless defined $required;
    _do_something_magic ($required);
    my $self = { 'foo' => $required };
    return bless $self, $pkg;
}

В этом случае ваш повторно благословленный объект может быть не тем, который вы ожидаете позже в коде.

Можно рассмотреть конструкторы со встроенной функциональностью «благословения». Но такие «объектные преобразователи» сделают проект еще более сложным.

Придерживайтесь базового определения: «Объект является экземпляром класса. Навсегда».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...