【问题标题】:How should I access instance data in a Perl subclass?我应该如何访问 Perl 子类中的实例数据?
【发布时间】:2009-08-05 12:22:54
【问题描述】:

我正在扩展一个模块,我想要一些关于良好做法的提示。特别是命名空间冲突:它们到底是什么以及如何避免它们。

在扩展时,我是否应该不访问 SUPER 类中的变量,而只能通过访问器或对象方法更改其状态?如果没有(或有限的)访问者怎么办?我是否“允许”直接访问这些对象变量?

干杯!

【问题讨论】:

    标签: perl


    【解决方案1】:

    最好只通过访问器访问事物,因为这样可以防止超类实现的更改影响子类。你应该远离任何以下划线开头的东西。这些东西是班级私有的。尽量远离任何没有记录的东西。依赖这些东西会让你陷入困境。另外,请考虑使用 has-a 与 is-a 关系。

    让我们想象一个小部件类。这个类有 name 和 price 成员(注意,这些都不是特别好的代码,我只是为了举例而放弃了一个版本,没有考虑它):

    package Widget;
    
    use strict;
    use warnings;
    
    sub new {
       my $class = shift;
       my %args  = @_;
    
       return bless {
           price => $args{price} || 0,
           name  => $args{name}  || "unkown",
       }, $class;
    }
    
    sub price { shift->{price} }
    sub name  { shift->{name}  }
    
    1;
    

    您决定子类化小部件以添加权重成员:

    package Widget::WithWeight;
    
    use strict;
    use warnings;
    
    use base 'Widget';
    
    sub new {
        my $class = shift;
        my %args  = @_;
        my $self  = $class->SUPER::new(%args);
        $self->{weight} = $args{weight} || 0;
        return bless $self, $class;
    }
    
    sub weight { shift->{weight} }
    
    sub price_per_pound {
        my $self = shift;
        return $self->{price}/$self->{weight};
    }
    
    1;
    

    现在假设第一个模块的作者改变了他/她关于如何存储价格的想法。也许它被存储为浮点数并且作者意识到将其存储为整数便士会更好:

    package Widget;
    
    use strict;
    use warnings;
    
    sub new {
       my $class = shift;
       my %args  = @_;
    
       if ($args{price}) {           
           $args{price} =~ s/[.]//;
       }
    
       return bless {
           price => $args{price} || "000",
           name  => $args{name}  || "unkown",
       }, $class;
    }
    
    sub price { 
        my $self = shift;
        my $price = $self->{price};
        substr($price, -2, 0) = ".";
        return $price;
    }
    
    sub name  { shift->{name}  }
    
    1;
    

    突然之间,您的测试将开始失败,但如果您改用 price 访问器,您将不会受到该更改的影响。

    【讨论】:

    • 我没有使用 _foobar 方法,但是,我直接访问了一些变量的内部表示,例如:现实生活中的值存储为这样的哈希引用: $self = { 'property' => { objnum => NUM, type => TYPE, value => VALUE }, } 我应该直接通过 $self 访问“property”,就像在 $self->{property} 中一样?如果它有一个 get_property() 访问器,我应该只访问它吗?在我的安装脚本中“要求”某个特定版本的 SUPER 类模块会是一件愚蠢的事情吗?
    • 如果它们没有存储在带有下划线的哈希中(例如$self->{_property},它们是公平的游戏,但访问器方法是一种更安全的方法(保护您免受实现更改)。要求最低版本不是一个坏主意。
    【解决方案2】:

    如果您从两个模块继承为一个模块并且它们都提供(导出)相同的子模块,则可能会发生命名空间冲突。

    我建议你看看 Moose,它是 Perl 的一个扩展,它为你提供类和角色。如果使用角色,可以避免很多冲突。见http://www.iinteractive.com/moose/

    Moose 还为类变量创建自动访问器,从而更安全地从继承类访问它们。

    【讨论】:

    • 我没有从两个模块继承,所以这不是真正的问题。我听说过 Moose 角色,它们看起来确实很有趣。但是,我正在扩展的模块不是我的(我不是作者),所以我不会使用 Moose 为我的子类创建访问器。不过感谢您的建议。
    • 注意,您可以使用Moose 子类化非Moose 类,因此没有什么可以阻止您使用它。我会建议MooseX::Declare 而不是香草Moose。好多了。
    • 我的意思是:我正在创建类 Foo::Bar,我没有创建 Foo,它是一个没有实现对其某些数据的访问器的类。我会与作者取得联系,看看他为什么不希望我戳那个数据......不过感谢 moosex 的提示!
    猜你喜欢
    • 2023-03-16
    • 1970-01-01
    • 2021-11-07
    • 2011-04-20
    • 2021-02-21
    • 2019-12-08
    • 2015-06-05
    • 1970-01-01
    • 2011-11-07
    相关资源
    最近更新 更多