36

我正在开发一个小的 Perl 模块,由于某种原因,我让使用我的新模块的测试驱动程序脚本调用了我认为是私有的函数之一,并且它成功了。我很惊讶,所以我开始搜索谷歌,我真的找不到任何关于如何在 Perl 模块中创建私有函数的文档......

我看到一个地方说在你的“私人”函数的右大括号后面放一个分号,像这样:

sub my_private_function {
...
}; 

我试过了,但我的驱动程序脚本仍然可以访问我想要私有的功能。

我将编写一个更简短的示例,但这就是我所追求的:

模块 TestPrivate.pm:

package TestPrivate;

require 5.004;

use strict;
use warnings;
use Carp;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

require Exporter;

@ISA = qw(Exporter AutoLoader);

our @EXPORT_OK = qw( public_function );
our @EXPORT    = qw( );

$VERSION = '0.01';

sub new {
    my ( $class, %args ) = @_;
    my $self = {};
    bless( $self, $class );
    $self->private_function("THIS SHOULD BE PRIVATE");
    $self->{public_variable} = "This is public";
    return $self;
}

sub public_function {
    my $self     = shift;
    my $new_text = shift;
    $self->{public_variable} = $new_text;
    print "Public Variable: $self->{public_variable}\n";
    print "Internal Variable: $self->{internal_variable}\n";
}

sub private_function {
    my $self     = shift;
    my $new_text = shift;
    $self->{internal_variable} = $new_text;
}

驱动程序:TestPrivateDriver.pl

#!/usr/bin/perl
use strict;
use TestPrivate 'public_function';
my $foo = new TestPrivate();
$foo->public_function("Changed public variable");
$foo->private_function("I changed your private variable");
$foo->public_function("Changed public variable again");
$foo->{internal_variable} = "Yep, I changed your private variable again!";
$foo->public_function("Changed public variable the last time");

驱动器输出:

Public Variable: Changed public variable
Internal Variable: THIS SHOULD BE PRIVATE
Public Variable: Changed public variable again
Internal Variable: I changed your private variable
Public Variable: Changed public variable the last time
Internal Variable: Yep, I changed your private variable again!

所以我在模块的最后一个右大括号后加了一个分号,但输出还是一样的。我真正发现的唯一一件事就是将此行作为第一行添加到我的 private_function 中:

caller eq __PACKAGE__ or die;

但这似乎很hacky。我没有很多编写 Perl 模块的经验,所以也许我的模块设置不正确?perl 模块中是否可以有私有函数和变量?

谢谢你帮助我学习!

4

9 回答 9

38

perldoc perltoot(大约四分之一的文件):

Perl 不限制谁可以使用哪些方法。公共与私有的区别是按照惯例,而不是语法。(好吧,除非您使用下面“作为变量的数据成员”中描述的 Alias 模块。)有时您会看到方法名称以下划线或两个下划线开头或结尾。这个标记是一个约定,表明这些方法只对那个类是私有的,有时对它最亲近的人来说是私有的,它的直接子类。但是这种区别并不是 Perl 本身强制执行的。这取决于程序员的行为。

因此,我建议您在“私有”方法的开头添加一个或两个下划线,以帮助阻止使用。

于 2009-01-16T19:09:02.990 回答
23

只有“The Kludge”将代码引用存储在词法变量中,该范围之外的任何人都无法看到:

my $priv_func1 = sub { my $self = shift; say 'func1'; };

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

而且我想不出一种方法来制作严格“受保护”的字段。

据我所知,就是这样(除了源过滤器......嘘。我没有提到它们......)


编辑:实际上,事实证明我可以想到一种非常混乱的受保护方式。但这可能涉及通过AUTOLOAD子传递所有调用。(!!)

于 2009-01-16T19:10:19.243 回答
15

这有效:

my $priv_func1 = sub {
    my $self = shift; say 'func1';
};

sub public_sub { 
    my $self = shift;

    $self->$priv_func1(@_);
}
于 2009-01-17T00:50:06.000 回答
9

只需检查来电者:

package My;

sub new {
  return bless { }, shift;
}

sub private_func {
  my ($s, %args) = @_;
  die "Error: Private method called"
    unless (caller)[0]->isa( ref($s) );

  warn "OK: Private method called by " . (caller)[0];
}

sub public_func {
  my ($s, %args) = @_;

  $s->private_func();
}

package main;

my $obj = My->new();

# This will succeed:
$obj->public_func( );

# This will fail:
$obj->private_func( );
于 2009-01-22T16:06:15.797 回答
6

你想做什么?也许有更好的 Perl 方式来做任何你想要完成的事情。

例如,如果您不想让人们在您的对象中乱搞,因为您想强制封装,您可以使用Class::InsideOut 之类的东西。该模块有一个 Class::InsideOut::About 文档模块来解释这个概念。还有Object::InsideOut,Brian Phillips 已经提到过。

于 2009-01-16T21:28:52.457 回答
3

一段时间后,当您意识到不能只使用 Data::Dumper 直接转储对象或窥视对象内部以查看其数据时,这种 OO 风格开始感觉有点“不成熟”。但是,如果您想试一试,我建议您使用 Object::InsideOut。它支持对象的私有数据和方法以及许多其他方便的功能(访问器生成、默认构造函数等)。

于 2009-01-16T19:43:07.040 回答
3

我们可以在 perl 私有函数下面写一些东西来检查是否来自同一个 obj 的调用作为caller[0]给包。

sub foo {
  my ($s, %args) = @_;
  die "Error: Private method called"
      unless (caller)[0]->isa( ref($s) );
}
于 2009-05-15T05:43:52.683 回答
2

如果您使用像Moose这样的系统,您可以获得公有/私有的区别,如此处所示

于 2009-05-15T21:42:14.363 回答
0

在您的包的文件中:将私有方法定义为 CODE-Ref,即:

my $private_methode = sub{};
于 2015-01-19T10:20:16.567 回答