tags:

views:

105

answers:

3
my $t=TT::Pop->new(name => "shadowfax", color => "white");
my $w=TT::Pop::Subs->new($t);

How to write TT::Pop::Subs::new() so that I can make $w cantains all the properties from $t ? In other words, I want to initialize a class with its super class.

In this case, all properties from $t should be readonly, so copy by ref is acceptable. (I am not sure whether perl can make those properties readonly)

A: 
sub new {
    my $this = shift;
    my $class = ref($this) || $this;
    my $SuperClass = shift;
    my $self = ref($SuperClass)?{%$SuperClass,}:{};
    bless $self, $class;
    return $self;
}

Well, this makes a copy of the hash.

Galaxy
You are shifting twice.
Ether
my $w=TT::Pop::Subs->new($t); So, scalar @_ == 2;
Galaxy
+1  A: 

Use Clone to make a deep copy of your data structure and bless the new hash:

use Clone qw(clone);

my $w = bless clone($t), 'TT::Pop::Subs';

Or if you want to create a copy constructor in TT::Pop::Subs:

# In TT::Pop
use base 'Clone'; 

# In TT::Pop::Subs
sub new {
  my ($class, $source) = @_;
  return bless $source->clone, $class;
}

Read more about clone here: http://search.cpan.org/~rdf/Clone-0.31/Clone.pm Of course there are many other modules that do the same thing (deep copies of data structures), just pick the one you like best.

jkramer
+1  A: 

First, don't call your method new call it promote or something like that.

I have mixed feelings about how to handle this design. My initial reaction is that you should use accessor methods and 'manually' copy the values into a new object.

sub promote {
    my $class = shift;
    my $obj   = shift;

    my %args = map { $_ => $obj->$_ } qw(
        attribute
        another
        this_one
        that_one
    );

    return $class->new( %args );
}

This insulates your subclass from changes in the parent.

Your parent class may now be or become an inside out object or something else that does not clone well. A critical attribute in the parent may be replaced by a method that generates it from other attributes.

Obviously, a degree of coupling between a parent and child class is unavoidable. But this approach helps minimize it.

The other way that seems obvious is to use a clone and rebless like jkramer suggests. The big problem with this is that you have these restrictions:

  • your parent class objects must be clonable
  • your parent and child classes must implement storage in identically.
  • accessor/mutator methods must not use a class-name prefix for attribute access. This means that class Foo stores attrib in $self->{'Foo::attrib'} = 'value'; while Foo::Bar stores them in $self->{'Foo::Bar::attrib'} = 'value';

For cloning, I would use Storable's dclone. It has the advantage of being a core module, and many more exotic object implementations provide hooks for Storable so that cloning will work properly.

daotoad
It should be better to explicit which property to copy if I want to make the class more universal.And your way looks more atomic.
Galaxy