如何在 Perl 中使用 OOP不管您是否相信,Perl 中的 OOP 对初级和中级用户都不难,甚至对高级用户也没那么复杂。根据我们到目前为止所讨论的有关 OOP的复杂工作方式,您可能不这么认为。然而,Perl 却乐意对程序员施加尽可能少的限制。Perl OOP就象烤肉(恕我比喻不当)。每个人都带来自己的肉,并以自己喜爱的方式烤肉。甚至还有烤肉的团队精神也是那样,就象可以轻易在不相关的对象之间共享数据一样。
我们必须采取的第一步是理解 Perl 包。包类似于 C++ 中的名称空间和 Java中的库:象用来将数据限制在特定区域的围栏。然而,Perl 包只是为程序员提供建议。缺省情况下,Perl不限制包之间的数据交换(尽管程序员可以通过词法变量这样做)。
清单 1. 包名、切换包、在包之间共享数据和包变量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #!/usr/bin/perl
# note: the following code will generate warnings with the -w switch,
# and won't even compile with "use strict". It is meant to demonstrate
# package and lexical variables. You should always "use strict".
# pay attention to every line!
# this is a global package variable; you shouldn't have any with "use strict"
# it is implicitly in the package called "main"
$global_sound = "
";
package Cow; # the Cow package starts here
# this is a package variable, accessible from any other package as $Cow::sound
$sound = "moo";
# this is a lexical variable, accessible anywhere in this file
my $extra_sound = "stampede";
package Pig; # the Pig package starts, Cow ends
# this is a package variable, accessible from any other package as $Pig::sound
$Pig::sound = "oink";
$::global_sound = "pigs do it better"; # another "main" package variable
# we're back to the default (main) package
package main;
print "Cows go: ", $Cow::sound; # prints "moo"
print "\nPigs go: ", $Pig::sound; # prints "oink"
print "\nExtra sound: ", $extra_sound; # prints "stampede"
print "\nWhat's this I hear: ", $sound; # $main::sound is undefined!
print "\nEveryone says: ", $global_sound; # prints "pigs do it better"
|
请注意,可以在所有三个包(“main”、“Pig”和“Cow”)中访问文件作用域内的词法变量 $extra_sound ,因为在该示例中它们是在同一文件中定义的。通常,每个包在它自己文件内部定义,以确保词法变量为该包所私有。这样就可以实现封装。(有关详细信息,请运行“ perldoc perlmod ”。)
接下来,我们要将包与类关联。就 Perl 而言,类只是一个奇特的包(相反,对象由 bless() 函数特别创建)。同样,Perl 对 OOP 规则实施得不是很严格,以便程序员不为其所约束。
new() 方法是类构造器的惯用名称(尽管按照 Perl 惯有的不严格方式,您可以使用任意名称)。当将类实例化成对象时都要调用它。
清单 2. barebones 类1
2
3
4
5
6
7
8
9
10
| #!/usr/bin/perl -w
package Barebones;
use strict;
# this class takes no constructor parameters
sub new
{
my $classname = shift; # we know our class name
bless {}, $classname; # and bless an anonymous hash
}
1;
|
可以通过将清单 2 中的代码放入任何目录内名为 Barebones.pm的文件中,然后在该目录中运行以下命令来测试该代码(这表示:“在库路径中包括当前目录,使用 Barebones模块,然后创建一个新的 Barebones 对象”):
1
| perl -I. -MBarebones -e 'my $b = Barebones->new()'
|
例如,可以在 new() 方法中放入 print 语句,以便看到 $classname 变量所拥有的内容。
如果调用 Barebones::new() 而不是 Barebones->new() ,类名将不会传递到 new() 。换句话说, new() 将不作为构造器,而只是普通的函数。
您可能要问:为什么需要传入 $classname 。为什么不直接用 bless {},"Barebones"; ?因为继承的缘故,这个构造器可能被一个从 Barebones 继承、但名称却不是 Barebones 的类调用。您可能正在用错误的名称享有错误的事,而在 OOP 中,那是个坏主意。
除了 new() 之外,每个类都需要成员数据和方法。定义它们就象编写几个过程一样简单。
清单 3. 带有成员数据和方法的类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #!/usr/bin/perl -w
package Barebones;
use strict;
my $count = 0;
# this class takes no constructor parameters
sub new
{
my $classname = shift; # we know our class name
$count++; # remember how many objects
bless {}, $classname; # and bless an anonymous hash
}
sub count
{
my $self = shift; # this is the object itself
return $count;
}
1;
|
可以用以下命令测试该代码:
1
| perl -I. -MBarebones -e 'my $b = Barebones->new(); Barebones->new(); print $b->count'
|
您应该得到 '2' 这个结果。构造器被调用两次,它修改词法变量( $count ),该变量被限制在 Barebones 包的作用域,而 不是每个 Barebones 对象的作用域。应该将对象本身范围内的数据存储在对象本身中。在 Barebones 的示例中,被享有成对象的是匿名散列。请注意我们怎样才能在每次调用该对象的方法时访问该对象,因为对该对象的引用是传递给那些方法的第一个参数。
有几个特殊的方法,例如 DESTROY() 和 AUTOLOAD() ,Perl在某些条件下会自动调用它们。 AUTOLOAD() 是用来允许动态方法名称的全捕获(catch-all)方法。 DESTROY() 是对象析构器,但是除非您确实非常非常需要,否则不应该使用它。在 Perl 中使用析构器通常表明您还在象 C/C++程序员那样考虑问题。
让我们看一下继承。在 Perl 中通过更改 @ISA 变量来这样做。您只需将一个类名表赋值给该变量即可。就是这样。您可以在 @ISA 中放入任何东西。您可以使您的类成为 Satan 的子类。Perl不在乎(尽管您的牧师、部长、教长、犹太学者等可能在乎)。
清单 4. 继承1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #!/usr/bin/perl -w
package Barebones;
# add these lines to your module's beginning, before other code or
# variable declarations
require Animal; # the parent class
@ISA = qw(Animal); # announce we're a child of Animal
# note that @ISA was left as a global default variable, and "use
# strict" comes after its declaration. That's the easiest way to do it.
use strict;
use Carp;
# make your new() method look like this:
sub new
{
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = $class->SUPER::new(); # use the parent's new() method
bless ($self, $class); # but bless $self (an Animal) as Barebones
}
1;
|
这些是 Perl 中 OOP 的最基本知识。Perl语言中还有很多您应该探索的知识。人们已经撰写了很多有关这一主题的书籍。如果想阅读的话,请参考 。
h2xs:您最好的新朋友您不想拥有一个可以为您编写 Perl类、还可以编写文档(POD)框架并且通常可以通过正确地完成这些事而使您的生活轻松一些的工具吗?Perl正好带有这种工具: h2xs 。
别忘了使用几个重要标志:“ -A -n Module ”。利用这些标志,h2xs 将生成一个名为“Module”、且里面全是有用文件的框架目录。这些文件是:
- Module.pm ,模块本身,带有已经编写好的框架文档。
- Module.xs ,用于将您的模块与 C 代码链接。(有关详细信息,请运行“ perldoc perlxs ”。)
- MANIFEST ,用于打包的文件列表。
- test.pl ,框架测试脚本。
- Changes ,对该模块所做更改的日志。
- Makefile.PL ,makefile 生成器(用“ perl Makefile.PL ”运行它。)
|