Как сделать частные функции в модуле Perl? - PullRequest
32 голосов
/ 16 января 2009

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

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

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?

Спасибо за помощь в обучении!

Ответы [ 9 ]

33 голосов
/ 16 января 2009

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

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

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

22 голосов
/ 16 января 2009

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

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

sub public_sub { 
    my $self = shift;

    $priv_func1->( $self );
}

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

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


РЕДАКТИРОВАТЬ: На самом деле, оказывается, я могу думать о очень грязный способ сделать защищенный. Но это, вероятно, потребует прохождения всех вызовов через подпрограмму AUTOLOAD. (!!)

14 голосов
/ 17 января 2009

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

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

sub public_sub { 
    my $self = shift;

    $self->$priv_func1(@_);
}
8 голосов
/ 22 января 2009

Просто проверьте звонящего:

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( );
6 голосов
/ 17 января 2009

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

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

3 голосов
/ 15 мая 2009

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

sub foo {
  my ($s, %args) = @_;
  die "Error: Private method called"
      unless (caller)[0]->isa( ref($s) );
}
3 голосов
/ 16 января 2009

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

2 голосов
/ 16 мая 2009

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

0 голосов
/ 19 января 2015

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

my $private_methode = sub{};
...