在脚本中,我初始化几个处理程序并设置应该可用于单独模块中的函数的变量。哪个是在模块中使用它们的最佳方式($q
,$dbh
,%conf
)?
伪模块示例:
package My::Module
sub SomeFunction (
@data = $dbh->selectrow_array("SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar} );
return $q->p( "@data" );
)
1;
伪脚本示例:
use CGI;
use DBI;
use My::Module;
our $q = new CGI;
our $dbh = some_connenction_function($dsn);
our %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );
我知道使用main::
命名空间会起作用,但是sholud是更清洁的方式吗?或者不是?
答案 0 :(得分:5)
打包My :: Module
您的模块应独立于上下文。也就是说,他们不应该期望$dbh
是数据库处理程序,或者他们应该在$q
中返回内容。或者该配置保存在%conf
中。
例如,如果您突然发现自己有两个数据库句柄实例,该怎么办?你是做什么?我讨厌模块要求我使用特定于模块的变量进行配置,因为这意味着我不能使用该模块的两个不同实例。
所以你有两个选择:
让我们使用您的伪代码查看第一个实例:
sub someFunction (
%param = @_;
%conf = %{param{conf};
@data = $param{dbh}->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
)
1;
伪脚本示例:
use CGI;
use DBI;
use My::Module;
my $q = new CGI;
my $dbh = some_connenction_function($dsn);
my %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );
someFunction (
q => $q,
dbh => $dbh,
conf => \%conf,
);
我在这里使用参数调用。还不错。是吗?现在,如果您需要另一个select语句,则可以使用不同的变量。
当然可以,但如果你不想一直传递变量怎么办?那么,您可以使用面向对象技术。现在,放松,冷静下来。使用面向对象设计有很多很好的理由:
my $foo = Foo::Bar->new;
类型的陈述。较新的Perl模块只有面向对象的接口,您无法使用它们。让我们看看面向对象的方法是如何工作的。首先,让我们看看主程序:
use CGI;
use DBI;
use My::Module;
my $q = new CGI;
my $dbh = some_connenction_function($dsn);
my %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );
my $mod_handle = My::Module->new (
q => $q,
dbh => $dbh,
conf => \%conf,
);
$mod_handle->someFunction;
在上文中,我现在创建一个包含这些变量的object instance
。而且,奇迹般地,我已将功能更改为 Methods 。方法只是 Class (aka模块)中的一个函数。诀窍在于我的实例(变量$mod_handler
将所有必需的变量存储得很漂亮而且整洁。$mod_hander->
语法只是将这些信息传递给我 functions 我的意思是方法。
那么,你的模块现在是什么样的?让我们看看第一部分,其中我有构造函数,它只是为我需要的变量创建存储的函数:
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->{Q} = $q;
$self->{DBH} = $dbh;
$self->{CONF} = $conf;
return $self;
}
让我们看一下有点不同的第一件事:my $class = shift;
。这是从哪里来的?当我使用语法Foo->Bar
调用函数时,我将Foo
作为函数Bar
中的第一个参数传递。因此,$class
等于My::Module
。就像我用这种方式调用你的函数一样:
my $mod_handle = My::Module::new("My::Module", %params);
而不是:
my $mod_handle = My::Module->new(%params);
接下来是my $self = {};
行。这是创建对哈希的引用。如果您不了解引用,则应该查看Perldocs中包含的Mark's Reference Tutorial。基本上,引用是存储数据的存储位置。在这种情况下,我的哈希没有名称,我所拥有的只是对存储的内存的引用,称为$self
。在Perl中,名称new
或$self
并没有什么特别之处,但它们是每个人都非常关注的标准。
bless
命令正在引用我的引用$self
,并将其声明为My::Module
类型。这样,Perl可以跟踪$mod_handle
是否是可以访问这些功能的实例的类型。
如您所见,$self
引用包含我的函数所需的所有变量。并且,我方便地将其传回我的主程序,我将其存储在$mod_handle
。
现在,让我们看一下 Methods :
sub SomeFunction {
$self = shift;
my $dbh = $self->{DBH};
my $q = $self->{Q};
my %conf = %{self->{CONF}};
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
再次,$self = shift;
行。请记住,我将其称为:
$mod_handle->SomeFunction;
与调用它相同:
My::Module::SomeFunction($mod_handle);
因此,$self
的值是我在$mod_handle
中存储的哈希引用。并且,该哈希引用包含我总是传递给该函数的三个值。
您永远不应在主程序和模块之间共享变量。否则,您不仅会在程序中每次都使用相同的名称,而且必须注意不要在程序的其他部分并行使用模块。
通过使用面向对象的代码,您可以在单个实例中存储所需的变量,并在同一实例中的函数之间来回传递它们。现在,您可以随意调用程序中的变量,并且可以在并行实例中使用模块。它可以改善您的计划和编程技能。
此外,你也可以使用面向对象编程,因为它不会消失。它运作得很好。整个语言都是专门面向对象的,如果你不了解它是如何工作的,那么你永远不会提高你的技能。
而且,我提到小鸡挖了吗?
在所有Perl黑客降临到我之前。我想提一下,我的Perl面向对象设计是非常糟糕。它比你想要的更好,但是它有一个严重的缺陷:我已经将我的对象的设计暴露给了我班上的所有方法。这意味着如果我改变存储数据的方式,我将不得不通过我的整个模块来搜索和替换它。
我这样做是为了保持简单,让我更清楚我在做什么。但是,正如任何一个好的面向对象的程序员会告诉你(和像我一样的二流黑客)是你应该使用setter / getter函数来设置你的成员值。
setter函数非常简单。模板看起来像这样:
sub My_Method {
my $self = shift;
my $value = shift;
# Something here to verify $value is valid
if (defined $value) {
$self->{VALUE} = $value;
}
return $self->{VALUE};
}
如果我在我的计划中致电$instance->My_Method("This is my value");
,则会将$self->{VALUE}
设置为This is my value
。同时,它返回$self->{VALUE}
。
现在,让我这样说吧:
my $value = $instance->My_Method;
我的参数$value
未定义,因此我没有设置值$self->{VALUE}
。但是,我仍然会返回该值。
因此,我可以使用相同的方法来设置和获取我的价值。
让我们看看我的构造函数(这是new
函数的一个奇特名称):
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->{Q} = $q;
$self->{DBH} = $dbh;
$self->{CONF} = $conf;
return $self;
}
不是直接在这个程序中设置$self->{}
哈希引用,好的设计说我应该使用这样的getter / setter函数:
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->Q = $q; #This line changed
$self->Dbh = $dbh; #This line changed
$self->Conf = $conf; #This line changed
return $self;
}
现在,我必须定义这三个子例程,Q
,Dbh
和Conf
,但现在我的SomeFunction方法就是这样:
sub SomeFunction {
$self = shift;
my $dbh = $self->{DBH};
my $q = $self->{Q};
my %conf = %{self->{CONF}};
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
对此:
sub SomeFunction {
$self = shift;
my $dbh = $self->Dbh; #This line changed
my $q = $self->Q; #This line changed
my %conf = %{self->Conf}; #This line changed
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
这些变化很微妙,但很重要。现在我的new
函数和我的SomeFunction
不知道这些参数是如何存储的。知道它们如何存储的唯一地方是getter / setter函数本身。如果我改变了类数据结构,除了getter / setter函数本身之外,我不需要修改任何东西。
这里有值得思考的东西......
如果您的所有SQL调用都在My :: Module函数中,那么为什么不首先简单地初始化$dbh
和$q
。这样,您就不需要在程序中包含Use Dbi;
模块。实际上,您的程序现在仍然无法确切地知道数据的存储方式。你不知道它是SQL数据库,Mongo数据库,还是一些扁平的Berkeley风格的数据库结构。
我包括a module我做了很久很久以前的工作,很久以前我试图简化我们使用数据库的方式。这个模块做了几件事:
看看它。它并不是最伟大的,但您将看到我如何使用面向对象的设计来消除您遇到的所有问题。要查看文档,只需在命令行中键入perldoc MFX:Cmdata
。
答案 1 :(得分:2)
使用main :: explicit非常干净
作为替代方案,您可以将该数据传递给模块构造函数方法,假设您的模块是基于对象的(或者从构造函数中的main ::将其复制到模块自己的变量中)。这样,不太完美的主要::隐藏在远离模块其余部分的地方。
答案 2 :(得分:2)
一种更简洁的方法是使用Exporter
从包中的其他命名空间创建符号。如果你只是偶尔的Perl程序员,我不会理会它,因为它有一些学习曲线,有很多陷阱,而对于玩具项目,它使代码更加复杂。但这对于大型项目和了解其他人的模块至关重要。
通常,包中有全局变量(或特别是全局函数),您希望在不同的模块中使用它而不对其进行限定(即,将package::
添加到每个变量和函数调用之前)。这是Exporter
可能用来做到这一点的一种方式:
# MyModule.pm
package MyModule;
use strict; use warnings;
use Exporter;
our @EXPORT = qw($variable function);
our $variable = 42; # we want this var to be used somewhere else
sub function { return 19 }; # and want to call this function
...
1;
# my_script.pl
use MyModule;
$variable = 16; # refers to $MyModule::variable
my $f = function(); # refers to &MyModule::function
你的问题规范是倒退的(并不是说它一定有什么问题) - 你希望主脚本和主包中的变量在另一个模块中可见。对于多次使用的变量/函数的简短列表,更简洁的方法可能是破解符号表:
package My::Module;
# required if you use strict . You do use strict , don't you?
our ($q, $dbh, %conf);
*q = \$main::q;
*dbh = \$main::dbh;
*conf = \%main::conf;
...
现在$My::Module::q
和$main::q
引用相同的变量,您可以在$q
或main
命名空间中使用My::Module
。