Perl的面向对象

Perl不是面向对象编程的最佳选择,Perl的面向对象机制是后来嫁接进去的,下面我们就看看是怎么回事。

  • 对象只是一个引用(也就是一个scalar变量),它恰好知道自己属于哪个类。要告诉一个引用它所指向的内容属于哪个类,使用[bless](http://perldoc.perl.org/functions/bless.html)。要知道引用所指向的内容属于哪个类(如果有的话),使用[ref](http://perldoc.perl.org/functions/ref.html)

  • 方法只是一个子程序,接受对象(或者对于类的方法,就是包名)作为第一个参数。使用$obj->method()可以调用对象的方法,用Package::Name->method()可以调用类的方法。(译者注:所谓类的方法,在其他语言里就相当于类的静态方法。)

  • 就是包含一组方法的包。

下面有个简短的例子来帮助我们弄清楚这些概念。示例模块Animal.pm包含Animal类,内容如下:

  1. use strict;
  2. use warnings;
  3. package Animal;
  4. sub eat {
  5. # 第一个参数总是操作所基于的对象
  6. my $self = shift @_;
  7. foreach my $food ( @_ ) {
  8. if($self->can_eat($food)) {
  9. print "Eating ", $food;
  10. } else {
  11. print "Can't eat ", $food;
  12. }
  13. }
  14. }
  15. # 就这个参数来说,假设动物可以吃任何东西
  16. sub can_eat {
  17. return 1;
  18. }
  19. return 1;

然后我们可以这样使用这个类:

  1. require Animal;
  2. my $animal = {
  3. "legs" => 4,
  4. "colour" => "brown",
  5. }; # $animal是一个普通的hash的引用
  6. print ref $animal; # "HASH"
  7. bless $animal, "Animal"; # 现在它是"Animal"类的对象
  8. print ref $animal; # "Animal"

注意:任何引用都可以被转换(bless)成任何类的对象。需要由你来保证(1)这个引用指向的内容可以被当做这个类的对象来使用,并且(2)被转换成的这个类存在,并且已经被加载了。

你仍然可以按以前的方式操作这个hash:

  1. print "Animal has ", $animal->{"legs"}, " leg(s)";

但你也可以同样用->运算符调用这个对象的方法,就像这样:

  1. $animal->eat("insects", "curry", "eucalyptus");

最后那句调用等价于Animal::eat($animal, "insects", "curry", "eucalyptus")

构造函数

构造函数是这个类返回新对象的方法。如果你需要,声明一个就是了,用你喜欢的任何名字都可以。对于类的方法,第一个参数是类名而不是一个对象,在这个例子里就是"Animal"

  1. use strict;
  2. use warnings;
  3. package Animal;
  4. sub new {
  5. my $class = shift @_;
  6. return bless { "legs" => 4, "colour" => "brown" }, $class;
  7. }
  8. # ...etc.

然后像下面这样使用:

  1. my $animal = Animal->new();

继承

要创建一个类继承自基类,用use parent,假设我们给Animal创建一个子类叫Koala,位于Koala.pm

  1. use strict;
  2. use warnings;
  3. package Koala;
  4. # 继承自Animal
  5. use parent ("Animal");
  6. # 重载一个方法
  7. sub can_eat {
  8. my $self = shift @_; # 没有使用,你也可以直接在这里写"shift @_;"
  9. my $food = shift @_;
  10. return $food eq "eucalyptus";
  11. }
  12. return 1;

下面是一些示例程序:

  1. use strict;
  2. use warnings;
  3. require Koala;
  4. my $koala = Koala->new();
  5. $koala->eat("insects", "curry", "eucalyptus"); # 只吃eucalyptus

最后那个方法调用尝试执行Koala::eat($koala, "insects", "curry", "eucalyptus"),但子程序eat()并没有在Koala包里定义。然而,因为Koala有父类Animal,Perl解释器会再尝试调用Animal::eat($koala, "insects", "curry", "eucalyptus"),这回没问题。请注意Animal类是如何自动被Koala.pm加载的。

因为use parent接受一组父类的名字,所以Perl支持多重继承,当然也就包含了它所带来的所有好处和噩梦。