开发者

How to reclassify Perl object

I'm working with a few Perl packages, we'll call them Some::Parser and Some::Data. A Some::Parser object has methods to return objects of type Some::Data. I have written a class that extends the Some::Data class, let's call it My::Data. Objects of class My::Data are really just objects of class Some::Data, but with additional methods that make it easier to work with.

My problem is that I want to continue to use the Some::Parser class to do the hard work of parsing the data. As I said earlier, Some::Parser objects give me Some::Data objects. Once I have a Some::Data object in hand, is there any way to reclassify it as a My::Data object? How would I do this?

I'm totally willing to change my approach, assuming someone can suggest a better way of doing what I want to do, but writi开发者_运维问答ng my own parser is not something I'm interested in doing!


This smells like a bit of a kludge. It might be time to rethink your strategy. For example, maybe you should write My::Parser which returns My::Data objects.

But if you don't want to do that, you can manually use bless to change an object's class:

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

See bless in perldoc.


Probably the best way to handle something like this is for Some::Parser to provide a way to specify the class it should be using for data objects. For example, HTML::TreeBuilder provides the element_class method. If you want TreeBuilder to produce something other than HTML::Element nodes, you subclass HTML::TreeBuilder and override element_class to return your desired node class. (The actual code in TreeBuilder is a bit more complex, because there was a different mechanism for doing this prior to HTML-Tree 4, and the new maintainer didn't want to break that.)

I take it that you didn't write Some::Parser, but perhaps it has this capability already. If not, maybe its maintainer will accept a patch. It should be a fairly simple change. You'd just add a data_class method (sub data_class { 'Some::Data' }), and then change Some::Data->new to $self->data_class->new. Then you can subclass Some::Parser to create My::Parser, and just override data_class.


You can rebless anything.

Inheritance in Perl 5 is nothing more than searching @ISA.


You can re-bless the returned object to whatever your heart desires:

#!/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;

Alternatively, you can use @cjm's idea:

#!/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;


I'd consider re-blessing venturesome. Once you have an object you can't really tell if it was created using its constructor ( usually Foo::new() ), or someone re-blessed some other object.

The problem is, some constructors are fat, this means they do a whole lotta more than just blessing something:

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;
}

In this case your re-blessed object might not be the one you'll expect later in code.

One may consider constructors with "re-blessing" functionality build in. But such "object converters" will make the design even more complicated.

Stick to the basic definition: "An object is an instance of the class. Forever.".

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜