Perlモジュールでプライベート関数を作成するにはどうすればよいですか?
-
19-08-2019 - |
質問
小さなPerlモジュールに取り組んでいますが、何らかの理由で、新しいモジュールを使用しているテストドライバースクリプトが、プライベートだと思った関数の1つを呼び出し、成功しました。びっくりしたので、グーグルで検索を始めましたが、Perlモジュールでプライベート関数を作成する方法に関するドキュメントを見つけることができませんでした...
<!> quot; private <!> quotの右中括弧の後にセミコロンを置くと言った場所を見ました。次のような関数:
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モジュールにプライベート関数とプライベート変数を含めることは可能ですか?
学習にご協力いただきありがとうございます!
解決
perldoc perltoot
から(ドキュメントの約4分の1):
Perlは、誰がどのメソッドを使用するかについて制限を課しません。の パブリックとプライベートの区別は、構文ではなく慣例によるものです。 (まあ、 以下の<!> quot; Data Membersで説明されているAliasモジュールを使用しない限り、 変数<!> quot ;.)時々、以下で始まるまたは終わるメソッド名が表示されます アンダースコアまたは2つ。このマーキングは、 メソッドはそのクラスのみにプライベートであり、時にはその最も近くにあります 知人、その直接のサブクラス。しかし、この区別は Perl自体によって強制されません。振る舞うのはプログラマー次第です。
したがって、<!> quot; private <!> quot;の先頭にアンダースコアを1つまたは2つ付けることをお勧めします。使用を思いとどまらせる方法。
他のヒント
<!> quot; The Kludge <!> quot;のみがあります。コード参照をレキシカル変数に保存する方法。そのスコープ外の誰も見ることができません:
my $priv_func1 = sub { my $self = shift; say 'func1'; };
sub public_sub {
my $self = shift;
$priv_func1->( $self );
}
そして quot; protected <!> quot;を厳密に作成する方法を考えることはできません。フィールド。
それは私の知る限りです(ソースフィルター以外... shhhh。それらについては言及しませんでした...)
編集:実際、非常に厄介な保護方法を考えることができます 。しかし、おそらくすべての呼び出しを AUTOLOAD
サブを介して渡す必要があります。 (!!)
これは動作します:
my $priv_func1 = sub {
my $self = shift; say 'func1';
};
sub public_sub {
my $self = shift;
$self->$priv_func1(@_);
}
呼び出し元を確認する:
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( );
何をしようとしていますか?たぶん、あなたが達成しようとしていることを何でもするより良いPerlの方法があるでしょう。
たとえば、カプセル化を強制するためにオブジェクトをいじりたくない場合は、 Class :: InsideOut 。そのモジュールには、概念を説明するClass :: InsideOut :: Aboutドキュメントモジュールがあります。また、 Object :: InsideOut もあります。これはブライアンフィリップスが既に言及しています。
このスタイルのOOは、少し<!> quot; un-perlish <!> quot;を感じ始めます。しばらくしてから、Data :: Dumperを使用してオブジェクトを直接ダンプしたり、オブジェクトの内部を覗いてデータを確認したりすることはできないことに気付きます。ただし、試してみたい場合は、 Object :: InsideOut 。オブジェクトのプライベートデータとメソッド、およびその他の便利な機能(アクセサー生成、デフォルトコンストラクターなど)をサポートしています。
caller[0]
と同じobjからの呼び出しがパッケージを提供するかどうかをチェックするために、perlプライベート関数に以下のことを書くことができます。
sub foo {
my ($s, %args) = @_;
die "Error: Private method called"
unless (caller)[0]->isa( ref($s) );
}
パッケージのファイル:プライベートメソッドをCODE-Refとして定義します。例:
my $private_methode = sub{};