2

我在 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 可以useClass_B 并创建一个实例作为属性。两者都有属性history_data,但如果 Class_B 是由 Class_A 的实例创建的实例,我希望 Class_Bhitory_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 值”?

4

3 回答 3

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

附言。请注意,如果 Class A 和 Class B 对象都持有对彼此的引用,那么您刚刚创建的是循环引用。Perl 只有简单的引用计数垃圾回收,不能自动检测这种引用循环;这意味着,如果您随后让两个对象超出范围而没有显式破坏循环中的至少一个链接,则这些对象将永远不会被垃圾收集(直到您的程序结束)并且您的代码最终会泄漏内存。

解决此问题的一种方法是削弱其中一个引用——通常是从 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 的垃圾收集方法不可避免的副作用。

于 2013-04-02T14:23:32.100 回答
3

不。一个对象只有在你为它定义和设置一个属性时才知道它的父对象。(在您的情况下是一个哈希条目。)Perl 不会在后台跟踪谁创建了谁。

于 2013-04-02T12:51:51.720 回答
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;
于 2013-04-02T17:28:51.727 回答