¿Cómo hago funciones privadas en un módulo Perl?
module perl-module (9)
¿Que estás tratando de hacer? Tal vez haya una mejor forma de Perl de hacer lo que sea que intentes lograr.
Por ejemplo, si no quiere que la gente hurgue en sus objetos porque quiere aplicar la encapsulación, puede usar algo como Class::InsideOut . Ese módulo tiene un módulo de documentación Class :: InsideOut :: About que explica el concepto. También está Object::InsideOut , que Brian Phillips ya mencionó.
Estoy trabajando en un pequeño módulo Perl y, por alguna razón, tenía el script del controlador de prueba que estaba usando mi nueva llamada al módulo, una de las funciones que creía que serían privadas, y fue exitosa. Me sorprendió, así que comencé a buscar en Google y no pude encontrar ninguna documentación sobre cómo hacer funciones privadas en los módulos de Perl ...
Vi un lugar que decía poner un punto y coma después de la llave de cierre de su función "privada", como esta:
sub my_private_function {
...
};
Intenté eso, pero mi script de controlador aún podía acceder a la función que quería que fuera privada.
Voy a inventar algo que será un ejemplo más breve, pero esto es lo que busco:
Módulo 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;
}
Controlador: 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");
Salida del controlador:
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!
Así que agregué un punto y coma después del último paréntesis de cierre en el módulo, pero el resultado sigue siendo el mismo. Lo único que encontré fue agregar esta línea como la primera línea de mi función privada:
caller eq __PACKAGE__ or die;
Pero eso parece bastante hacky. No tengo mucha experiencia en la escritura de módulos Perl, entonces ¿quizás estoy configurando mi módulo incorrectamente? ¿Es posible tener funciones y variables privadas en módulos perl?
¡Gracias por ayudarme a aprender!
Desde perldoc perltoot
(aproximadamente a la cuarta parte del documento):
Perl no impone restricciones sobre quién puede usar qué métodos. La distinción pública versus privada es por convención, no por sintaxis. (Bueno, a menos que use el módulo Alias que se describe a continuación en "Miembros de datos como variables"). Ocasionalmente, verá nombres de métodos que comienzan o terminan con un guión bajo o dos. Esta marca es una convención que indica que los métodos son privados solo para esa clase y, a veces, para sus conocidos más cercanos, sus subclases inmediatas. Pero esta distinción no se aplica por el propio Perl. Depende del programador comportarse.
Por lo tanto, le recomiendo que ponga un guión bajo o dos al principio de sus métodos "privados" para ayudar a disuadir el uso.
En Archivo para su paquete: defina métodos privados como CODE-Ref, es decir:
my $private_methode = sub{};
Este estilo de OO comienza a sentirse un poco "perverso" después de un tiempo cuando te das cuenta de que no puedes usar Data :: Dumper para volcar el objeto directamente o echar un vistazo dentro del objeto para ver sus datos. Sin embargo, si quieres darle una oportunidad, te recomiendo usar Object::InsideOut . Es compatible con datos privados y métodos para sus objetos junto con una serie de otras características útiles (generación de acceso, constructor predeterminado, etc.).
Esto funciona:
my $priv_func1 = sub {
my $self = shift; say ''func1'';
};
sub public_sub {
my $self = shift;
$self->$priv_func1(@_);
}
Podemos escribir algo a continuación en la función privada perl para verificar si la llamada del mismo obj que caller[0]
da el paquete.
sub foo {
my ($s, %args) = @_;
die "Error: Private method called"
unless (caller)[0]->isa( ref($s) );
}
Simplemente revise la persona que llama:
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( );
Solo existe "The Kludge" para almacenar una referencia de código en una variable léxica, que nadie fuera de ese alcance puede ver:
my $priv_func1 = sub { my $self = shift; say ''func1''; };
sub public_sub {
my $self = shift;
$priv_func1->( $self );
}
Y no puedo pensar en una forma de hacer campos rigurosamente "protegidos".
Hasta donde sé, eso es todo (además de los filtros fuente ... shhhh. No los mencioné ...)
EDITAR: En realidad, resulta que puedo pensar en una forma muy desordenada de hacer protegida. Pero probablemente implicaría pasar todas las llamadas a través del sub de AUTOLOAD
. (!!)