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(@_);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值