perl对象

#!/usr/bin/perl


=pod [1]对象就是一个引用 [2]类就是包 [3]方法就是子例程 =cut


#方法调用
=pod 对于类方法而言,调用者是包的名字 对于实例方法而言,调用者是指定对象的引用 =cut

#名词解释 INVOCANT 方法调用者

#[1] 使用箭头操作符的方法调用
=pod INVOCANT->METHOD(LIST) INVOCANT->METHOD 调用者->方法 如果INVOCANT是一个用,我们就称这个方法是以实例方法调用的 如果INVOCANT是一个包的名字,我们把这个被调用的METHOD看作类方法 =cut


use DBI;

$dbh = DBI->connect($data_source, $username, $auth, \%attr); #类方法
$dbh->do($statement);  #实例方法


#learning

$image = Wizard->summod("Gandalf"); #类方法
$image->speak("friend");  #实例方法

#summod and speak 都是由Wizard类定义的

#合并
$image->summod("Gandalf")->speak->("friend");


#[2] 使用间接对象方法的方法调用
=pod METHOD INVOCANT(LIST) METHOD INVOCANT LIST METHOD INVOCANT =cut

$image = summod Wizard "Gandalf"; 
$nameis = summod Balrog home => "Moria", weapon => "whip";
move $nameis "bridge";
break $staff;


#[3] 引用包的类
=pod 使用间接调用容易混淆 有一种方法可以解决这样的混淆,而同时仍然保持间接对象的语法:通过在包后面附加两个冒号引用类名 $obj = method CLASS:: #强制为"CLASS->method" #下面的方法我们经常看到: $obj = new CLASS #不会解析为方法 =cut


$obj = new ElvenRing;  #可以是new("ElvenRing")
                       #也可以是new(ElveRing())

$obj = ElverRing->new;  #可以是ElvenRing()->new

$obj = new ElvenRing::;  #总是"ElvenRing"->new()
$obj = ElvenRing::->new; #总是"ElvenRing"->new()



#[4]构造对象
=pod 所有的对象都是引用,但不是所有的引用都是对象. 把引用和包名字标记起来(因此也和包中的类标记起来了,因为一类就是一个包)的动作 被称作bless,你可以把bless看作是把引用转换成对象,尽管更准确地说是把该引用转换 成一个对象引用。 bless函数接受一个或者两个参数.第一个是参数是引用,而第二个是把要引用bless成的包。 如果忽略第二个参数,则使用当前包. =cut


$obj = {};                #指把引用放到一个匿名散列
bless($obj);              #bless散列到当前包
bless($obj,"Critter");    #bless 散列到类Critter

#一个典型的构造器:
package Critter;
sub spawn {bless{};}

#或者略明确的拼写
package Critter;
sub spwan {
    my $ref = {};          #指向一个空的匿名散列
    bless $ref,"Cirtter";  #把那个散列制作一个Cirtter对象
    return $ref;           # 返回新生成的Cirtter
}  

$pet = Cirtter-spwan;


#[5] 可继承构造器
=pod 和所有方法一样,构造器只是一个子例程,但我们不把它看作子例程,在这个例子中,我们总是把它当作方法 来调用----一个类方法,因为调用者是一个包名字。 =cut


=pod 方法调用 | 结果子例程调用 --------------------------------------------------- Cirtter->spwan() | Cirtter::spwan("Cirtter") Spider->spwan() | Cirtter::spwan("Spider") --------------------------------------------------- 两种情况调用的子例程都是一样的,但是参数不一样。请注意我们上面 的spwan构造器完全忽略了它的参数,这就意味着我们的Spider对象被错 误地bless成了Cirtter类。一个更好的更好的构造器将提供包名字给bless: =cut

sub spwan {
   my $class = shift;      #存储包的名字
   my $ref = {};    
   bless($ref,$class);     #bless该引用到包中
   return $ref;
}

#现在你可以为两种情况使用同一个子例程
$vermin = Cirtter->spwan;
$shelob = Spider->spwan;

#对象或者类名一起工作

sub spwan {
   my $invocant = shift;
   my $class    = ref($invocant) ||$invocant;
   my $self = {};
   bless ($self,$class);
   return $self;
}



#[6]初启程序
=pod 大多数对象维护的信息是由对象的方法间接操作的。到目前为止我们所有的构造器都创建了 空散列,但是我们没有理由让它们这么空着。比如,我们可以让构造器接受额外的参数,并且把 它们当作键/值。有关面向技术把这些称为“性质(Property)”,"属性(attribute)","存储器(accessor)" "成员属性(member data)","实例数据(instance date)"或者"实例变量(instance variable)" =cut


#假设一个Horse类有一些实例属性,如"name" 和 "color":
$steed = Horse->new(name => 'shadowfax', color => "white");



#如果该对象是用散列引用实现的,那么一旦引用者从参数列表删除,那么
#键/值对就可以插进散列:

sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self = {@_};   #剩下的参数变成属性
    bless($self,$class);  #给予对象性质
    return $self;
}


=pod 这回我们为该类的构造器使用一个名字叫new的方法,就这样可以使用那些C++程序 员相信这些都是正常的东西.不过perl可不这么认为"new"有任何特殊的地方;你可以 把构造器命名为任意的东西.任何碰巧创建和返回对象的方法都是实际意义上的构造 器。 =cut

sub new {
   my $invocant = shift;
   my $class = ref($invocant) ||$invocant;
   my $self = {
      color => "bay",
      legs => 4,
      owner => undef,
      @_,    #覆盖以前的属性
   };
   return bless $self,$class;
}

$eg = Horse->new;                        #四腿栗马
$stallion = Horse->new(color => "black") #黑色的四腿栗马


=pod 把这个Horse构造器当作实例方法使用时,它将忽略它的调用者现在有的属性, 你可以设计第二个构造器,把它当作实例方法来调用,如果设计得合理,你就可以 使用来调用者的值作为新生成的对象的缺省值: =cut

$steed = Horse->new(color => 'dun');
$foal  = $steed->clone(owner => 'EquuGen Guild,Ltd');


sub clone {
    my $model = shift;
    my $self = $model->new(%$model,@_);
    return $self;   #前面被->new bless过了
}


#[7] 类继承
=pod 当你调用一个方法的时候,如果perl在调用者的包里找不到这个子 例程,那么它就检查@ISA数组。 Perl是这样实现继承的:一包的@ISA数组里的每个元素都保存另一个包的名字,当 缺失方法的时候就搜索这些包。比如,下面的代码把Horse类变成Cirtter类的子类. =cut


#我们必须用our声明@ISA,因为它必须是一个打包的变量,而不是my声明词

package Horse;
our @ISA = "Cirtter";

#当你调用了调用者的一个类型为classname的方法methodname时,
#Perl尝试六种不同的方法来找出所用的子例程
=pod 1.首先,Perl在自己的包里找一个叫classname::methodname的子例程,如果失败, 则进入继承,并进入步骤2. 2.第二步,Perl通过检查@classname::ISA中列出的所有的父包,检查从基类继承过 来的方法,看看有没有parent::methodname子例程.这种搜索从左向右,递归,深度 优先进行的.递归保证祖父类,曾祖父类,太祖父类等等类都进行搜索 3.如果仍然失败,Perl就搜索一个叫UNIVERSAL::methodname的子例程. 4.这时,perl放弃methodname然后开始查找AUTOLOAD.首先,它检查叫做class::AUTOL OAD的子例程。 5.如果上面的步骤失败,perl则搜索的有在@classname::ISA中列出的parent包,寻找 任何parnet::AUTOLOAD子例程.这样搜索仍然是从左向右,递归,深度优先进行的。 6.最后,perl寻找一个叫UNIVERSAL::AUTOLOAD的例程。 =cut

#perl会在找到的第一个子例程处停止并调用该例程。如果没有找到,则产生一个异常:

'Can't locate object method "methname" via package "classname"';'


#通过@ISA继承

package  Mule;
use base ("Hourse","donkey");   #声明一个父类


package Mule;
BEGIN {
        our @ISA = qw(Hourse donkey);
        require Hourse;
        require donkey;
}


#访问被覆盖的方法
=pod 定义类方法时,该例程会覆盖任意基类中同名的方法.想象一下你有一个Mule 对象(它是人人Horse类和Donkey类派生出来的),而且你想调用你的对象的breed 方法.尽管父类有它们自己的breed方法,Mule类设计者通过给Mule类写自己的breed 方法覆盖了父类那些的breed方法。这就意味着下面交叉引用可能不能正常的工作: =cut

$stallion = Hourse->new(gender => 'male');
$molly    = Mule->new(gender => 'female');
$colt     = $moly->breed($stallion);

#明确确保传递者的调用者:
$solt = $molly->Horse::Breed($stallion);


=pod 有时候,你希望一个派生类的方法表现得像基类中某些方法的包装.实际上 派生类的方法本身就可以调用基类的方法,在调用前或调用后增加自己的动作. 你可以这种表示法只用于演示指定从哪个类开始搜索.但是在使用覆盖方法大 多数情况下,你并不希望自己要知道或指定该执行哪个父类被覆盖的方法。 这就是SUPER伪类提供便利的地方.它令你能够调用一个覆盖了的基类的方法, 而不用指定哪个类定义了该方法。 =cut

#下面的子例程在当前包的@ISA中查找,而不要求指定特定的类:
package Mule;
our @ISA = qw(Horse Donkey);
sub kick {
   my $self = shift;
   print "The mule kicks\n";
   $self->SUPPER::kick(@_);
}


=pod 出现多重继承时,SUPPER并不是总是按你想的那样运行.它也像@ISA那样 遵循普通的继承机制的规则:从左向右,递归,深度优先.如果Horse和Donkey 都有一个speak方法,而你需要使用Donkey的方法,你不得不明确命名该 父类: =cut

sub speak {
   my $self = shift;
   print "The mule speak\n";
   $self->Donkey::speak(@_);
}


你可能感兴趣的:(对象,perl,Class,实例)