Подтвердить что ты не робот

Как сделать частные функции в модуле Perl?

Я работаю над небольшим модулем Perl, и по какой-то причине у меня был тестовый драйвер script, который использовал мой новый модуль для вызова одной из функций, которые, как я думал, были бы частными, и это было успешным. Я был удивлен, поэтому начал искать google, и я не мог найти документацию о том, как создавать частные функции в Perl-модулях...

Я увидел одно место, которое произвело точку с запятой после закрывающей фигурной скобки вашей функции "private", например:

sub my_private_function {
...
}; 

Я пробовал это, но мой драйвер script мог получить доступ к функции, которую я хотел быть приватной.

Я сделаю что-то, что будет более коротким, но вот что мне нужно:

Модуль 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!

Итак, я добавил точку с запятой после последней замыкающей фигурной скобки в модуле, но результат все тот же. Единственное, что я действительно нашел, это добавить эту строку в первую строку к моей частной_функции:

caller eq __PACKAGE__ or die;

Но это кажется довольно взломанным. У меня нет большого опыта написания модулей Perl, так что, возможно, я неправильно настроил свой модуль? Возможно ли иметь частные функции и переменные в perl-модулях?

Спасибо, что помогли мне учиться!

4b9b3361

Ответ 1

От perldoc perltoot (примерно четверть пути через документ):

Perl не накладывает ограничений на то, кто получает какие-то методы. разделение "public-versus-private" проводится по соглашению, а не по синтаксису. (Что ж, если вы не используете модуль Alias, описанный ниже в разделе "Члены данных как Переменные".) Иногда вы увидите имена методов, начинающиеся или заканчивающиеся на подчеркивание или два. Эта маркировка - это соглашение, указывающее, что методы являются частными только для этого класса, а иногда и для самого близкого знакомых, его непосредственных подклассов. Но это различие не выполняемый самим Perl. Это зависит от того, как программист должен себя вести.

Поэтому я рекомендую вам добавить символ подчеркивания или два в начале ваших методов "private", чтобы помочь отговорить использование.

Ответ 2

Существует только "The Kludge" для хранения ссылки на код в лексической переменной, которую никто не видит вне этой области:

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

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

И я не могу придумать способ сделать строго "защищенные" поля.

Что это, насколько я знаю (помимо исходных фильтров... shhhh. Я не упоминал их....)


EDIT: На самом деле, оказывается, я могу думать о очень грязном способе защиты. Но это, вероятно, связано с передачей всех вызовов через AUTOLOAD. (!!)

Ответ 3

Это работает:

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

sub public_sub { 
    my $self = shift;

    $self->$priv_func1(@_);
}

Ответ 4

Просто проверьте вызывающий:

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

Ответ 5

Что вы пытаетесь сделать? Возможно, есть лучший способ Perl делать то, что вы пытаетесь выполнить.

Например, если вы не хотите, чтобы люди обманывали ваши объекты, потому что вы хотите обеспечить инкапсуляцию, вы можете использовать что-то вроде Class:: InsideOut. Этот модуль имеет модуль документации Class:: InsideOut:: About, который объясняет концепцию. Существует также Object:: InsideOut, о котором уже говорил Брайан Филлипс.

Ответ 6

Этот стиль OO начинает чувствовать себя немного "не-perlish" через некоторое время, когда вы понимаете, что не можете просто использовать Data:: Dumper, чтобы сбрасывать объект напрямую или заглянуть внутрь объекта, чтобы посмотреть его данные. Однако, если вы хотите сделать снимок, я бы рекомендовал использовать Object::InsideOut. Он поддерживает частные данные и методы для ваших объектов, а также ряд других удобных функций (генерация доступа, конструктор по умолчанию и т.д.).

Ответ 7

В частной функции perl мы можем написать несколько вещей, чтобы проверить, что вызов из того же obj, что и caller[0], дает пакет.

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

Ответ 8

Если вы используете такую ​​систему, как Moose, вы можете получить общедоступное/частное различие, как показано здесь.

Ответ 9

В файле для вашего пакета: определите частные методы как CODE-Ref, т.е.:

my $private_methode = sub{};