Perl Moose类中的私有变量

4

我正在学习使用Moose在Perl中创建对象。

我不确定我是否理解了的用途。请考虑:

use v5.14;

package PA {
    use Moose;
    my $var='private?';
    1;

    sub getVar {
        return $var;
    }
}

package PB {
    use Moose;
    use MooseX::Privacy;

    has 'var' => (
        is => 'rw',
        isa => 'Str',
        default   => 'private?',
        traits => [qw/Private/],
    );
    1;

    sub getVar {
        my $self = shift;
        return $self->var;
    }
}

my $o1= PA->new();
my $o2= PB->new();

say $o1->getVar();
say $o2->getVar();

在类 PAPB 中,我有一个私有变量 var。只有在类 PB 中,我使用了 MooseX::Privacy。这两种方法有什么区别?为什么我应该使用 MooseX::Privacy

2
没有从构造函数参数进行初始化,没有类型检查,没有强制转换,没有继承,没有组合,没有内省,没有添加特性的能力... - ikegami
PA的$var是静态的,而PB的不是。除此之外,PA是否在第一时间使用Moose呢?如果您不了解Moose的优势,那么整个问题都是无意义的。 - rutter
@ikegami 我只是在谈论私有变量,所以我不会在这些上使用构造函数初始化吗? - Håkon Hægland
@rutter 我没有在 PA 中包含公共变量,以使示例最小化。 - Håkon Hægland
不确定。我手头没有 Moose 进行测试。这个列表更多是为了提供一个想法而不是其他的东西。你的问题基本上是“Moose 提供了什么优势?” - ikegami
1个回答

5
如果您正在寻找类似Java的方法隐私性,那么MooseX::Privacy会让您大失所望。以下是Java风格方法隐私性的情况:
/* This file is called Main.java */
public class Main
{
    public class MyParent
    {
        private String message_string ()
        {
            return "Message from %s\n";
        }

        public void print_message ()
        {
            System.out.printf( this.message_string(), "MyParent" );
        }
    }

    public class MyChild extends MyParent
    {
        public String message_string ()
        {
            return "Another message from %s\n";
        }
    }

    public static void main (String[] args)
    {
        Main o = new Main();
        o.run();
    }

    public void run ()
    {
        MyParent c = new MyChild();
        c.print_message();
    }
}

你可以像这样编译和运行此示例:
$ javac Main.java
$ java Main
Message from MyParent

请注意发生了什么。父类(MyParent)声明message_string()为私有方法。子类试图覆盖该方法,但被坚决拒绝 - 子类,你不能这样做!

现在让我们尝试使用Perl和MooseX::Privacy进行等效操作...

# This file is called Main.pl
use v5.14;
use strict;
use warnings;

package MyParent {
    use Moose;
    use MooseX::Privacy;

    private_method message_string => sub {
        my $self = shift;
        return "Message from %s\n";
    };

    sub print_message {
        my $self = shift;
        printf($self->message_string(), __PACKAGE__);
    }
}

package MyChild {
    use Moose; extends qw(MyParent);
    use MooseX::Privacy;

    sub message_string {
        my $self = shift;
        return "Another message from %s\n";
    }
}

my $c = new MyChild();
$c->print_message();

我们可以这样运行:
$ perl Main.pl
Another message from MyParent

说实话,message_string不是应该是私有的吗?!MyChild怎么可能重写MyParent中的方法?!

事实是,MooseX::Privacy并不能像大多数面向对象语言中实现的方法私有化一样为你提供任何接近的东西。MooseX::Privacy只是类似于在你的方法中这样做:

die "GO AWAY!!" unless caller eq __PACKAGE__;

除非你想在所有方法调用时增加巨大的运行时开销,否则不要使用MooseX::Privacy。

事实上,使用MooseX::Privacy的理由很少。如果你想要私有方法,可以将它们放在词法变量中。就像这样:

use v5.14;
use strict;
use warnings;

package MyParent {
    use Moose;

    my $message_string = sub {
        my $self = shift;
        return "Message from %s\n";
    };

    sub print_message {
        my $self = shift;
        printf($self->$message_string(), __PACKAGE__);
    }
}

package MyChild {
    use Moose; extends qw(MyParent);

    sub message_string {
        my $self = shift;
        return "Another message from %s\n";
    }
}

my $c = new MyChild();
$c->print_message();

现在运行它:
$ perl Main2.pl
Message from MyParent

哈利路亚!! 我们有了一个真正的私有方法!

好的,所以你可以没有MooseX::Privacy而拥有私有方法,它们比MooseX::Privacy更好(更快)。

但是私有属性怎么办?嗯,我在CPAN上有一个小模块可以帮助你:Lexical::Accessor。这是一个小工具,为您创建一个属性,使用“内部存储”(即属性值不会存储在对象的blessed哈希引用中),并在词法变量中安装其访问器(就像上面的私有$get_message方法一样)。

无论如何,这是我对MooseX::Privacy的看法。


谢谢您的有趣回答!如果print_message方法在子类中而不是父类中定义,会发生什么?(或者如果它在子类中被覆盖了呢?) - Håkon Hægland
如果在MyChild中定义了print_message,那么它总是可以调用在MyChild中同样被定义的get_message(无论get_message选择了什么隐私)。如果在MyChild中定义了print_messageget_message是在MyParent中定义的私有方法,则在Java中(并且使用我的词法技术),它不能调用get_message,因为它甚至看不到get_message的存在!使用MooseX::Privacy,它可以看到它,并调用它,但这样做会抛出异常。 - tobyink

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接