Jak zrobić prywatne funkcje w module Perla?

Pracuję nad małym modułem Perla i z jakiegoś powodu miałem skrypt sterownika testowego, który używał mojego nowego modułu wywołującego jedną z funkcji, które myślałem, że będą prywatne, i udało się. Byłem zaskoczony, więc zacząłem szukać w google i nie mogłem znaleźć żadnej dokumentacji, jak tworzyć prywatne funkcje w modułach Perla...

Widziałem jedno miejsce, które mówiło, aby umieścić średnik po nawiasie zamykającym Twojej "prywatnej" Funkcji, Tak:

sub my_private_function {
...
}; 

Próbowałem to, ale mój skrypt sterownika nadal mógł uzyskać dostęp do funkcji, którą chciałem być prywatny.

Wymyślę coś, co będzie krótszym przykładem, ale oto, o co mi chodzi:

Moduł 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;
}

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

Wyjście sterownika:

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!

Dodałem średnik po ostatnim nawiasie zamykającym w module, ale wyjście jest nadal takie samo. Jedyne co znalazłem to dodać tę linijkę jako pierwsza linia do mojej private_function:

caller eq __PACKAGE__ or die;
Ale to wydaje się dość trudne. Nie mam dużego doświadczenia w pisaniu modułów Perla, więc może źle ustawiam swój moduł? Czy możliwe jest posiadanie prywatnych funkcji i zmiennych w modułach Perla?

Dzięki za pomoc w nauce!

Author: Greg Bacon, 2009-01-16

9 answers

Z perldoc perltoot (około ćwierć drogi przez dokument):

Perl nie nakłada ograniczeń na to, kto może używać jakich metod. Na rozróżnienie publiczno-prywatne jest przez Konwencję, a nie składnię. (Dobrze, chyba że użyjesz modułu Alias opisanego poniżej w "Data Members as Zmienne".) Czasami zobaczysz nazwy metod zaczynające się lub kończące na podkreślenie lub dwa. Oznaczenie to jest konwencją wskazującą, że metody są prywatne Tylko dla tej klasy i czasami do najbliższego znajomych, jego bezpośrednie podklasy. Ale to rozróżnienie jest nie egzekwowane przez sam Perl. Zachowanie programisty zależy od niego.

Dlatego polecam umieścić podkreślenie lub dwa na początku swoich "prywatnych" metod, aby pomóc zniechęcić użycie.

 33
Author: jedihawk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-17 00:48:03

Istnieje tylko "Kludge" przechowywania odniesienia do kodu w zmiennej leksykalnej, którego nikt poza tym zakresem nie może zobaczyć:

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

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

I nie mogę myśleć o sposobie na rygorystycznie "chronione" pola.

To tyle z tego co wiem ( poza filtrami źródłowymi...shhhh. Nie wspominałem o nich.... )


EDIT: W zasadzie, okazuje się, że ja mogę myśleć o bardzo niechlujnym sposobie robienia ochrony. Ale prawdopodobnie wiązałoby się to z przepuszczaniem wszystkich połączeń przez na AUTOLOAD sub. (!!)

 21
Author: Axeman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-19 20:01:14

To działa:

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

sub public_sub { 
    my $self = shift;

    $self->$priv_func1(@_);
}
 14
Author: Leon Timmermans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-17 00:50:06

Wystarczy sprawdzić:

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
Author: JDrago,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-22 16:44:23

Co próbujesz zrobić? Może istnieje lepszy sposób Perla na robienie tego, co próbujesz osiągnąć.

Na przykład, jeśli nie chcesz, aby ludzie kręcili się wokół Twoich obiektów, ponieważ chcesz wymusić enkapsulację, możesz użyć czegoś w rodzaju Class::InsideOut. Moduł ten posiada klasę::InsideOut::O module dokumentacji, która wyjaśnia tę koncepcję. Istnieje również Object::InsideOut, o którym Brian Phillips już wspomniał.

 6
Author: brian d foy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-16 21:28:52

Ten styl OO zaczyna czuć się trochę "nie-perlish" po pewnym czasie, kiedy zdajesz sobie sprawę, że nie możesz użyć Data::Dumper do zrzutu obiektu bezpośrednio lub zajrzeć do wnętrza obiektu, aby zobaczyć jego dane. Jeśli jednak chcesz spróbować, polecam użycie Object::InsideOut. Obsługuje Prywatne Dane i metody dla Twoich obiektów wraz z szeregiem innych przydatnych funkcji (generowanie dostępu, domyślny konstruktor, itp.).

 3
Author: Brian Phillips,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-16 20:58:31

Możemy napisać coś poniżej w prywatnej funkcji Perla, aby sprawdzić, czy wywołanie jest wywołane z tego samego obj, co caller[0] daje pakiet.

sub foo {
  my ($s, %args) = @_;
  die "Error: Private method called"
      unless (caller)[0]->isa( ref($s) );
}
 3
Author: Xaerxess,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-17 12:56:57

Jeśli używasz systemu takiego jak Moose, możesz uzyskać rozróżnienie publiczne/prywatne, jak widać tutaj .

 2
Author: Chris Simmons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-14 16:11:45

W pliku dla Twojego pakietu: Zdefiniuj metody prywatne jako CODE-Ref, tzn.:

my $private_methode = sub{};
 0
Author: Rolf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 12:56:50