webentwicklung-frage-antwort-db.com.de

Wie erstelle ich private Funktionen in einem Perl-Modul?

Ich arbeite an einem kleinen Perl-Modul und aus irgendeinem Grund ließ ich das Testtreiberskript, das mein neues Modul verwendete, eine der Funktionen aufrufen, die ich für privat hielt, und es war erfolgreich. Ich war überrascht, also fing ich an, nach Google zu suchen, und konnte keine Dokumentation darüber finden, wie man private Funktionen in Perl-Modulen erstellt ...

Ich habe eine Stelle gesehen, an der nach der schließenden Klammer Ihrer "privaten" Funktion ein Semikolon steht:

sub my_private_function {
...
}; 

Ich habe das versucht, aber mein Treiberskript konnte immer noch auf die Funktion zugreifen, die ich privat haben wollte.

Ich werde etwas erfinden, das ein kürzeres Beispiel sein wird, aber hier ist, wonach ich strebe:

Modul 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;
}

Treiber: 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");

Treiberausgabe:

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!

Also habe ich nach der letzten schließenden Klammer im Modul ein Semikolon eingefügt, aber die Ausgabe ist immer noch dieselbe. Das einzige, was ich wirklich gefunden habe, war, diese Zeile als erste Zeile zu meiner private_function hinzuzufügen:

caller eq __PACKAGE__ or die;

Aber das scheint ziemlich abgedreht zu sein. Ich habe nicht viel Erfahrung mit dem Schreiben von Perl-Modulen. Vielleicht richte ich mein Modul falsch ein? Ist es möglich, private Funktionen und Variablen in Perl-Modulen zu haben?

Danke, dass du mir geholfen hast zu lernen!

32
BrianH

Von perldoc perltoot (ungefähr ein Viertel des Dokuments):

Perl schränkt nicht ein, wer welche Methoden verwenden darf. Die Unterscheidung zwischen öffentlich und privat erfolgt nach Konvention, nicht nach der Syntax. (Nun, es sei denn, Sie verwenden das unten unter "Datenelemente als Variablen" beschriebene Alias-Modul.) Gelegentlich werden Methodennamen angezeigt, die mit einem oder zwei Unterstrichen beginnen oder enden. Diese Kennzeichnung ist eine Konvention, die angibt, dass die Methoden nur für diese Klasse und manchmal für ihre engsten Bekannten, ihre unmittelbaren Unterklassen, privat sind. Diese Unterscheidung wird jedoch nicht von Perl selbst erzwungen. Es liegt an dem Programmierer, sich zu verhalten.

Aus diesem Grund empfehle ich, ein oder zwei Unterstriche am Anfang Ihrer "privaten" Methoden zu setzen, um die Verwendung zu verhindern.

33
jedihawk

Es gibt nur "The Kludge" zum Speichern einer Codereferenz in einer lexikalischen Variablen, die niemand außerhalb dieses Bereichs sehen kann:

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

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

Und ich kann nicht denke über eine Möglichkeit nach, streng "geschützte" Felder zu erstellen.

Soweit ich weiß, ist es das. (Abgesehen von Quellfiltern ... pssst. Ich habe sie nicht erwähnt ...)


EDIT: Eigentlich stellt sich heraus, dass ich kann mir eine sehr chaotische Art und Weise vorstellen, geschützt zu sein. Aber es würde wahrscheinlich bedeuten, dass alle Aufrufe durch das Unter AUTOLOAD geleitet werden. (!!)

22
Axeman

Das funktioniert:

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

sub public_sub { 
    my $self = shift;

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

Prüfe einfach den Anrufer:

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

Was versuchst du zu machen? Vielleicht gibt es eine bessere Perl-Methode, um das zu tun, was Sie erreichen wollen.

Wenn Sie beispielsweise nicht möchten, dass sich Personen in Ihren Objekten aufhalten, weil Sie die Kapselung erzwingen möchten, können Sie Folgendes verwenden: Class :: InsideOut . Dieses Modul verfügt über ein Class :: InsideOut :: About-Dokumentationsmodul, das das Konzept erläutert. Es gibt auch Object :: InsideOut , das Brian Phillips bereits erwähnt hat.

6
brian d foy

Wir können etwas in die Perl-Funktion private schreiben, um zu überprüfen, ob der Aufruf vom selben Objekt stammt, das caller[0] package gibt.

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

Dieser Stil von OO fühlt sich nach einer Weile etwas "unperlisch" an, wenn Sie feststellen, dass Sie nicht einfach Data :: Dumper verwenden können, um das Objekt direkt zu sichern oder einen Blick in das Objekt zu werfen, um es zu betrachten Daten. Wenn Sie es jedoch versuchen möchten, würde ich die Verwendung von Object :: InsideOut empfehlen. Es unterstützt private Daten und Methoden für Ihre Objekte sowie eine Reihe weiterer nützlicher Funktionen (Accessor-Generierung, Standardkonstruktor usw.).

3
Brian Phillips

Wenn Sie ein System wie Elch verwenden, können Sie eine öffentliche/private Unterscheidung wie hier erhalten.

2
Chris Simmons

In Datei für Ihr Paket: Definieren Sie private Methoden als CODE-Ref, d. H .:

my $private_methode = sub{};
0
Rolf