Reputation: 3751
I've used/created lots of classes in Perl and one common thing I need to do in them is to access certain properties of a parent object. For example, let's say I have Class_A and Class_B and they are as simple as this:
Class_A.pm
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_B.pm
package Class_B;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_A can use
Class_B and create an instance as a property. Both have the property history_data
, but if Class_B is an instance created by an instance of Class_A, I want Class_B to use hitory_data
in its parent object.
So, what I've done all this time is to just pass a reference when I create the child instance, like this:
#!/usr/bin/perl
use strict;
use warnings;
use Class_A;
use Class_B;
my $class_a = new Class_A;
$class_a->{instance_of_B} = new Class_B parent => $class_a;
I'm doing that very simple, but that instance could be created by some method inside Class_A, when needing to use Class_B. The thing here is that, although Class_B has it's own properties, I want it to use the properties of Class_A when it is its parent.
That works well, but I've several times wondered if it exists a way to do that without passing the parent reference to the child. something like an already implemented way in Perl to call the parent object.
So, that's my question. Is there a way for Class_B to access the instance of its parent Class_A without receiving the reference directly?
Thanks. :-)
EDIT: Another way to put it is this:
Is there a way for Class_B, without having a reference passed, to say "I'm currently an instance of Class_B, living in a property of Class_A, which currently has x, y and z values on its own properties"?
Upvotes: 2
Views: 3052
Reputation: 118128
There seems to be a design issue somewhere here. However, see if Aspect::Library::Wormhole might help.
Depending on the exact circumstances, using a dependency injection framework such as Beam::Wire might help.
I am not advocating the code below as a "good" solution or even "good" practice, but here is how Wormhole
might help:
package A;
use strict; use warnings;
sub new {
my $class = shift;
bless {
b => undef,
history_data => [],
} => $class;
}
sub set_b {
my $self = shift;
$self->{b} = B->new;
return $self->{b};
}
package B;
use strict; use warnings;
sub new {
my $class = shift;
my $owner = shift;
bless {
history_data => $owner->{history_data} // [],
} => $class;
}
sub add_event {
my $self = shift;
push @{ $self->{history_data} }, [ @_ ];
return;
}
package main;
use strict; use warnings;
use Aspect;
use YAML;
aspect Wormhole => 'A::add_b', 'B::new';
my $x = A->new;
my $y = $x->set_b;
$y->add_event(Horizon => 180, 0, 'Terminal');
print Dump $x;
print Dump $y;
Upvotes: 1
Reputation: 50328
As Michael Carman correctly notes, if you want the Class B object to know which Class A object it is part of, you have to tell it that explicitly.
What you can at least do, however, is to simplify the user interface by having Class A take care of creating its Class B components, e.g. like this:
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
$this->{instance_of_B} = Class_B->new( parent => $this );
return $this;
}
Ps. Note that, if the Class A and Class B objects both hold a reference to each other, then what you've just created is a circular reference. Perl only has simple reference-counting garbage collection, which cannot automatically detect such reference circles; this means that, if you then let both objects go out of scope without explicitly breaking at least one link in the circle, the objects will never be garbage-collected (until your program ends) and your code ends up leaking memory.
One way to work around this issue is to weaken one of the references — normally the one from B to A, assuming that the Class A object is the one actually referenced by external code — like this:
package Class_B;
use strict;
use warnings;
use Scalar::Util qw(weaken);
sub new {
my $class = shift;
my $this = { @_ };
weaken $this->{parent};
bless $this, $class;
}
Note that, as a side effect, if someone grabs a reference directly to the Class B object, lets its parent Class A object go out of scope and then tries to call the Class B object's methods directly, the Class B object may find that its parent object has simply disappeared and the reference to it has become undefined. This is, unfortunately, an unavoidable side effect of dealing with Perl's method of garbage collection.
Upvotes: 4
Reputation: 30831
No. An object only knows about it's parent object if you define and set an attribute for it. (A hash entry, in your case.) Perl doesn't track who created whom in the background.
Upvotes: 3