如何在Perl中获取父类实例

时间:2013-04-02 11:00:44

标签: perl perl-module

我在Perl中使用/创建了很多类,我需要做的一件事就是访问父对象的某些属性。例如,我们说我有Class_A和Class_B,它们就像这样简单:

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可以use Class_B并创建一个实例作为属性。两者都具有属性history_data,但如果Class_B是由Class_A实例创建的实例,我希望Class_B在其父对象中使用hitory_data

所以,我一直在做的就是在创建子实例时传递引用,如下所示:

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

我这么做非常简单,但是当需要使用Class_B时,可以通过Class_A中的某个方法创建该实例。这里的问题是,虽然Class_B拥有它自己的属性,但我希望它在它的父级时使用Class_A的属性。

这很有效,但我曾多次想知道如果没有将父引用传递给孩子,是否存在这样做的方法。类似于Perl中已经实现的方式来调用父对象。

那么,这就是我的问题。是否有一种方法可以让Class_B直接访问其父Class_A的实例而不直接接收引用?

感谢。 : - )

编辑:另一种说法是:

对于没有引用的Class_B,有没有办法说"我目前是Class_B的一个实例,生活在Class_A的属性中,当前有x,y和z值它自己的属性"?

3 个答案:

答案 0 :(得分:4)

正如Michael Carman正确指出的那样,如果你想让B类对象知道它所属的A类对象,你必须明确告诉它。

然而,至少可以做的是通过让A类负责创建其B类组件来简化用户界面,例如:像这样:

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

聚苯乙烯。请注意,如果A类和B类对象都拥有彼此的引用,那么您刚刚创建的是circular reference。 Perl只有简单的引用计数垃圾收集,它不能自动检测这样的引用循环;这意味着,如果然后让两个对象超出范围而没有明确地破坏圆圈中的至少一个链接,则对象将永远不会被垃圾收集(直到程序结束)并且您的代码最终会泄漏内存。

解决此问题的一种方法是weaken其中一个引用 - 通常是从B到A的引用,假设A类对象是外部代码实际引用的对象 - 如下所示:

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

注意,作为一个副作用,如果有人直接抓取对B类对象的引用,让其父类A对象超出范围,然后尝试直接调用B类对象的方法,B类对象可能发现它的父对象已经消失,对它的引用变得不确定。不幸的是,这是处理Perl垃圾收集方法的不可避免的副作用。

答案 1 :(得分:3)

没有。如果为对象定义并设置属性,则对象仅知道它的父对象。 (在您的情况下,哈希条目。)Perl不会跟踪谁在后台创建了谁。

答案 2 :(得分:1)

这里似乎有一个设计问题。但是,看看Aspect::Library::Wormhole是否有帮助。

根据具体情况,使用依赖注入框架(如Beam::Wire)可能有所帮助。

更新

我不是在提倡下面的代码作为“好的”解决方案,甚至是“好”的做法,但这里Wormhole可能会有所帮助:

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;