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

w.k*_*w.k 0 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;
Run Code Online (Sandbox Code Playgroud)

伪脚本示例:

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() );
Run Code Online (Sandbox Code Playgroud)

我明白使用main::命名空间会起作用,但是sholud是更清洁的方式吗?或不?

Dav*_* W. 5

package 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;
Run Code Online (Sandbox Code Playgroud)

伪脚本示例:

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,
);
Run Code Online (Sandbox Code Playgroud)

我在这里使用参数调用.还不错.是吗?现在,如果您需要另一个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;
Run Code Online (Sandbox Code Playgroud)

在上面,我现在创建一个object instance包含这些变量.而且,奇迹般地,我已将您的功能更改为方法.方法只是(aka模块)中的一个函数.诀窍在于我的实例(变量$mod_handler将所有必需的变量存储得很漂亮而且整洁.$mod_hander->语法只是将这些信息传递给我的函数我的意思是方法.

那么,你的模块现在是什么样的?让我们看看我有构造函数的第一部分,它只是为我需要的变量创建存储的函数:

sub new {
   my $class = shift;
   my %param = @_;

   my $self = {};
   bless $self, $class

  $self->{Q} = $q;
  $self->{DBH} = $dbh;
  $self->{CONF} = $conf;

  return $self;
}
Run Code Online (Sandbox Code Playgroud)

让我们看一下有点不同的第一件事:my $class = shift;.这是从哪里来的?当我用语法调用函数时Foo->Bar,我Foo作为函数中的第一个参数传递Bar.因此,$class等于My::Module.就像我用这种方式调用你的函数一样:

my $mod_handle = My::Module::new("My::Module", %params);
Run Code Online (Sandbox Code Playgroud)

代替:

my $mod_handle = My::Module->new(%params);
Run Code Online (Sandbox Code Playgroud)

接下来就是这my $self = {};条线.这是创建对哈希的引用.如果您不理解参考文献,您应该查看Perldocs中包含的Mark的参考教程.基本上,引用是存储数据的存储位置.在这种情况下,我的哈希没有名称,我所拥有的只是对存储它的内存的引用$self.在Perl中,没有什么特别的名称new或者$self,但它们是每个人都非常遵循的标准.

bless命令正在引用我的引用$self,并将其声明为类型My::Module.这样,Perl可以跟踪是否$mod_handle是可以访问这些功能的实例类型.

如您所见,$self引用包含我的函数所需的所有变量.而且,我方便地将其传回我的主程序,我将其存储在其中$mod_handle.

现在,让我们来看看我的方法:

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" );
}
Run Code Online (Sandbox Code Playgroud)

再一次,那条$self = shift;线.记住我称之为:

 $mod_handle->SomeFunction;
Run Code Online (Sandbox Code Playgroud)

这与调用它是一样的:

 My::Module::SomeFunction($mod_handle);
Run Code Online (Sandbox Code Playgroud)

因此,值$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};
}
Run Code Online (Sandbox Code Playgroud)

如果我打电话给$instance->My_Method("This is my value");我的程序,它将设置$self->{VALUE}This is my value.同时,它返回值$self->{VALUE}.

现在,让我说这就是这样:

 my $value = $instance->My_Method;
Run Code Online (Sandbox Code Playgroud)

我的参数$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;
}
Run Code Online (Sandbox Code Playgroud)

$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;
}
Run Code Online (Sandbox Code Playgroud)

现在,我不得不定义这三个子程序Q,DbhConf,但现在我的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" );
}
Run Code Online (Sandbox Code Playgroud)

对此:

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" );
}
Run Code Online (Sandbox Code Playgroud)

这些变化是微妙的,但很重要.现在我的new功能和我SomeFunction不知道这些参数是如何存储的.知道它们如何存储的唯一地方是getter/setter函数本身.如果我改变了类数据结构,除了getter/setter函数本身之外我不需要修改任何东西.

后记

这是值得深思的......

如果你的所有SQL调用都在你的My :: Module函数中,那么为什么不简单地初始化它$dbh,然后在$q那里初始化.这样,您就不需要Use Dbi;在程序本身中包含该模块.实际上,您的程序现在仍然无法确切地知道数据的存储方式.你不知道它是一个SQL数据库,一个Mongo数据库,甚至是一些扁平的Berkeley风格的数据库结构.

我在很久以前的工作中包含了一个模块,我试图简化我们使用数据库的方式.这个模块做了几件事:

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

看看吧.它不是最好的,但你会看到我如何使用面向对象的设计来消除你遇到的所有问题.要查看文档,只需在perldoc MFX:Cmdata命令行中输入.