当前位置: 首页 > 图文教程 > 脚本技术 > Perl > Perl5 OOP学习笔记

Perl
不错的一篇学习CGI脚本(脚本)
详细说明什么是Perl
perl简单变量 整型 浮点数 字符串
perl操作符详细说明
perl 列表和数组变量详解
perl 文件读写详细说明
perl 模式匹配参数详解
perl 控制结构 条件控制 if while
perl 子程序 sub
Shell编程的一些知识集合
perl 学习资料整理篇
PERL脚本 学习笔记

Perl5 OOP学习笔记


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-11   浏览: 83 ::
收藏到网摘: n/a

在Perl排名持续下降的情况下学Perl,似乎是有点不明智。但是,工作需要,不得不学啊。再说,Perl现在在测试领域还是用得非常多的。Phython虽然也在测试领域开始活跃起来,不过我始终还是不太喜欢Phython的语法。 在学习了Perl的基本语法之后,学习Perl的OOP,略有心得。不知道Perl各个版本之间OOP是否有区别,但是我是学习的Perl5,所以在标题上将版本号也写出来了。因为了解到PHP4和PHP5的OOP部分就有不小的差别,所以有此担心。
  学习Perl的OOP,最关键的两件事情就是package和bless。只要把这两个东西搞清楚也就学会大一半了。
Perl的package
  感觉Perl的package和Java还真有点相似。Java的package是以CLASSPATH中的目录为根,按目录定义和搜索分级包名。Perl也类似,是以@INC数组中的目录为根,按目录搜索分级包名。不过有一点不同,Perl的package定义貌似不需要与目录结构对应。具体是什么样的规则我没有去研究,因为按目录结构定义package是个好习惯。
  相较于Java,Perl的package还有一点很有意思。Java的每层package对应一个目录,而最后是一个class文件对应到类名。Perl却简化了,package直接就把目录和文件名都引用了进去。比如
  Java中,name.jamesfancy.MyClass,对应的是/name/jamesfancy/MyClass.class,源代码中则分成两句来写
复制代码 代码如下:
复制代码 代码如下:

# test.pl
package MyClass;
sub new {
my $this = {};
bless($this);
}
package main;
my $obj1 = MyClass::new();
my $obj2 = MyClass->new();
my $obj3 = new MyClass();
print(join("\n", ref($obj1), ref($obj2), ref($obj3)));
__END__
MyClass
MyClass
MyClass

注意上面new MyClass()的效果和MyClass->new()效果是一样的。这里new不是关键字,而是函数名。同理,如果有一个foo成员函数的话,也可以foo MyClass(args),它实际上是MyClass::foo(MyClass, args);
  话说回来,如果需要初始化对象数据又该如何呢?前面说过,对象数据保存在引用的数据自身,所以我们通常是把一个Hash引用bless成对象。所以我们经常会看到这样调用new:
复制代码 代码如下:

my $obj = MyClass->new('key1' => 'value1', 'key2' => 'value2');

或者
复制代码 代码如下:

my $obj = MyClass->new({'key1' => 'value1', 'key2' => 'value2'});

两种调用方式的区别在于new函数中的处理不同,因为前者传入的是一个Hash实体,而后者传入的是一个Hash引用。为了兼容这两种情况,new函数通常会像下面程序中的写法:
复制代码 代码如下:

# test.pl
package MyClass;
sub new {
my $class = shift();
my $this = ref(@_[0]) ? @_[0] : {@_};
bless($this);
}
package main;
use Data::Dumper;
my $obj1 = MyClass->new('name' => 'James Fancy', 'age' => 30);
my $obj2 = MyClass->new({'name' => 'James Fancy', 'age' => 30});
print(Dumper($obj1));
print(Dumper($obj2));
__END__
$VAR1 = bless( {
'name' => 'James Fancy',
'age' => 30
}, 'MyClass' );
$VAR1 = bless( {
'name' => 'James Fancy',
'age' => 30
}, 'MyClass' );

访问对象数据
  既然通常是Hash引用被bless成对象,那就只说这种情况。
  既然是Hash引用,所以访问数据最简单的办法就跟访问Hash引用一样。比如
复制代码 代码如下:

$obj->{'name'} = "You Name";
my $name = $obj->{'name'};
$obj->{'name'} = "You Name";
my $name = $obj->{'name'};

  如果想少写点花括号,可以通过定义setter/getter的办法来解决。因为getter和setter可以根据有没参数来区分,所以合并在一个函数中成为可能,比如下面的name函数
复制代码 代码如下:

# test.pl
package MyClass;
sub new {
my $class = shift();
my $this = ref(@_[0]) ? @_[0] : {@_};
bless($this);
}
sub name {
my $this = shift();
if (@_[0]) {
$this->{'name'} = @_[0];
}
return $this->{'name'};
}
package main;
my $obj = MyClass->new('name' => 'James Fancy');
print($obj->name, "\n");
print($obj->name("New Name"), "\n");
__END__
James Fancy
New Name
# test.pl
package MyClass;
sub new {
my $class = shift();
my $this = ref(@_[0]) ? @_[0] : {@_};
bless($this);
}
sub name {
my $this = shift();
if (@_[0]) {
$this->{'name'} = @_[0];
}
return $this->{'name'};
}
package main;
my $obj = MyClass->new('name' => 'James Fancy');
print($obj->name, "\n");
print($obj->name("New Name"), "\n");
__END__
James Fancy
New Name

  使用setter/getter的确可以使程序看起来简洁不少。但是对对象中的每个数据写一个getter/setter,还是很累人的,于是,AUTOLOAD函数就被抬出来了,看看下面的程序
复制代码 代码如下:

package MyClass;
sub new {
my $class = shift();
my $this = ref(@_[0]) ? @_[0] : {@_};
bless($this, $class);
}
sub AUTOLOAD {
my $this = $_[0];
if (!ref($this)) {
return;
}
my $name = $AUTOLOAD;
if (defined($name)) {
$name =~ s/.*:://;
} else {
return;
}
my $class = ref($this);
if (defined($this->{$name}) || @_) {
no strict 'refs';
*{"${class}::$name"} = sub {
my $this = shift();
$this->{$name} = shift() if (@_);
# make a property in hash reference type to HashObject object.
if (ref($this->{$name}) eq 'HASH') {
bless($this->{$name}, $class);
}
return $this->{$name};
};
goto &$name;
}
}
package main;
my $obj = MyClass->new('name' => 'James Fancy');
$obj->more1({'key', 'value of more1->key'});
print($obj->name, "\n");
print($obj->more1->key, "\n");
print($obj->more2({})->key("value of more2->key"), "\n");
__END__
James Fancy
value of more1->key
value of more2->key

这样调用起来是不是方便多了?不过AUTOLOAD写起来很累人的。如果你只需要一个数据对象,网上有个Hash::AsObject的类很好用,用法和上面的最后一个示例差不多。
继承
  我的确是对继承这个方面没怎么研究。不过简单的继承大概就是用use base语句引入基类而已,比如
复制代码 代码如下:

package Parent;
sub test1 {
print("Parnet::test1\n");
}
sub test {
print("Parent::test\n");
}
package Sub;
use base Parent;
sub test {
print("Sub::test\n");
}
sub test2 {
$_[0]->Parent::test();
}
package main;
my $obj = bless({}, *Sub);
$obj->test();
$obj->test1();
$obj->test2();
__END__
Sub::test
Parnet::test1
Parent::test

参考资料

东南大学出版社出版,O'Reilly的《精通Perl(影印版)》,brian d foy著
Perl version 5.10.0 documentation,http://perldoc.perl.org/
Hash::AsObject源码,来自http://search.cpan.org/~nkuitse/Hash-AsObject-0.11/lib/Hash/AsObject.pm