如何在模块中使用main :: data?

时间:2011-12-01 22:52:58

标签: perl perl-module

在脚本中,我初始化几个处理程序并设置应该可用于单独模块中的函数的变量。哪个是在模块中使用它们的最佳方式($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是更清洁的方式吗?或者不是?

3 个答案:

答案 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语句,则可以使用不同的变量。

当然可以,但如果你不想一直传递变量怎么办?那么,您可以使用面向对象技术。现在,放松,冷静下来。使用面向对象设计有很多很好的理由:

  • 它可以简化您的编程:这会让人感到困惑,因为面向对象编程意味着考虑您的程序如何工作,然后设计它,然后创建各种对象。你想要做的就是编写代码并将其排除在外。事实是,通过考虑您的程序和设计它可以使您的程序更好地工作,并且您可以更快地编写代码。设计方面可以避免主要代码的复杂性,并将其安全地放在小的,易于理解的程序中。
  • Perl中很酷的东西:您将不得不使用面向对象的Perl,因为这是其他人正在编写的内容。您将看到许多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中存储的哈希引用。并且,该哈希引用包含我总是传递给该函数的三个值。


结论

您永远不应在主程序和模块之间共享变量。否则,您不仅会在程序中每次都使用相同的名称,而且必须注意不要在程序的其他部分并行使用模块。

通过使用面向对象的代码,您可以在单个实例中存储所需的变量,并在同一实例中的函数之间来回传递它们。现在,您可以随意调用程序中的变量,并且可以在并行实例中使用模块。它可以改善您的计划和编程技能。

此外,你也可以使用面向对象编程,因为它不会消失。它运作得很好。整个语言都是专门面向对象的,如果你不了解它是如何工作的,那么你永远不会提高你的技能。

而且,我提到小鸡挖了吗?

Adium的

在所有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;
}

现在,我必须定义这三个子例程,QDbhConf,但现在我的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我做了很久很久以前的工作,很久以前我试图简化我们使用数据库的方式。这个模块做了几件事:

  • 它处理了有关数据库的所有内容。初始化和所有句柄。因此,你的主程序不必使用任何模块,但是这个。
  • 它也远离开发人员编写select语句。相反,您定义了所需的字段,并找出了如何为您执行查询。
  • 它返回一个 incriminator函数,它用于从数据库中获取下一行。

看看它。它并不是最伟大的,但您将看到我如何使用面向对象的设计来消除您遇到的所有问题。要查看文档,只需在命令行中键入perldoc MFX:Cmdata

答案 1 :(得分:2)

  1. 使用main :: explicit非常干净

  2. 作为替代方案,您可以将该数据传递给模块构造函数方法,假设您的模块是基于对象的(或者从构造函数中的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引用相同的变量,您可以在$qmain命名空间中使用My::Module