web-dev-qa-db-ja.com

Perlモジュールでプライベート関数を作成するにはどうすればよいですか?

私は小さなPerlモジュールに取り組んでおり、何らかの理由で、新しいモジュールを使用しているテストドライバースクリプトに、プライベートだと思われる関数の1つを呼び出してもらい、成功しました。私は驚いたので、グーグルを検索し始めましたが、Perlモジュールでプライベート関数を作成する方法についてのドキュメントを見つけることができませんでした...

次のように、「private」関数の右中括弧の後にセミコロンを置くと言った場所が1つありました。

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;

しかし、それはかなりハックに思えます。 Perlモジュールの作成経験はあまりないので、モジュールを正しく設定していないのでしょうか? Perlモジュールにプライベート関数と変数を含めることは可能ですか?

私を学ぶのを助けてくれてありがとう!

34
BrianH

perldoc perltootから(ドキュメント全体の約1/4):

Perlは、誰がどのメソッドを使用できるかについて制限を課していません。公開と非公開の区別は慣例によるものであり、構文ではありません。 (以下の「変数としてのデータメンバー」で説明されているAliasモジュールを使用しない限り。)場合によっては、メソッド名が1つまたは2つのアンダースコアで開始または終了することがあります。このマーキングは、メソッドがそのクラスだけに、場合によっては最も近い知人、その直接のサブクラスにプライベートであることを示す規則です。しかし、この区別はPerl自体によって強制されるものではありません。振る舞うのはプログラマ次第です。

したがって、使用を思いとどまらせるために、「プライベート」メソッドの先頭に1つまたは2つのアンダースコアを付けることをお勧めします。

36
jedihawk

コード参照をレキシカル変数に格納する「The Kludge」だけがあり、そのスコープ外の誰も見ることができません。

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

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

また、厳密に「保護された」フィールドを作成する方法を考えることはできません。

それは私が知る限りそれです(ソースフィルター以外に... shhhh。私はそれらについて言及しませんでした....)


編集:実際、私は保護された非常に厄介な方法を考えることができます。しかし、おそらくすべての呼び出しを AUTOLOAD サブを介して渡す必要があります。 (!!)

22
Axeman

これは機能します:

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

sub public_sub { 
    my $self = shift;

    $self->$priv_func1(@_);
}
14
Leon Timmermans

発信者を確認してください:

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( );
8
JDrago

あなたは何をしようとしているのですか?たぶん、あなたが達成しようとしていることを何でも行うより良いPerlの方法があるでしょう。

たとえば、カプセル化を強制したいという理由でオブジェクトをいじくり回したくない場合は、 Class :: InsideOut のようなものを使用できます。そのモジュールには、概念を説明するClass :: InsideOut :: Aboutドキュメントモジュールがあります。ブライアンフィリップスがすでに述べた Object :: InsideOut もあります。

6
brian d foy

Perlプライベート関数に以下のように記述して、caller[0]と同じobjからの呼び出しでパッケージが提供されるかどうかを確認できます。

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

このスタイルのOOは、Data :: Dumperを使用してオブジェクトを直接ダンプしたり、オブジェクトの内部を覗いたりすることはできないことに気付いたとき、しばらくすると少し「無味」に感じ始めます。そのデータを見てください。ただし、試してみたい場合は、 Object :: InsideOut を使用することをお勧めします。オブジェクトのプライベートデータとメソッド、およびその他の便利な機能をサポートしています。 (アクセサーの生成、デフォルトのコンストラクターなど)。

3
Brian Phillips

Moose のようなシステムを使用している場合は、 here のようにパブリック/プライベートの区別を得ることができます。

2
Chris Simmons

パッケージのファイル内:プライベートメソッドをCODE-Refとして定義します。

my $private_methode = sub{};
0
Rolf