对象(上): 对象是一个数据结构,带有一些行为。 如果需要区分上面两种情况, 那么我们就把适用于某一特定对象的方法叫做实例 方法,而把那些适用于整个类的方法叫做类方法。不过这样做只是为了方便——对于 Perl 而言,方法就是方法,只是由其第一个参数的类型来区分。 类方法 第一个参数为类 对象方法 第一个参数为对象 类里面那些生成对象实例的方 法叫构造方法 Perl的对象系统: 一个对象只不过是一个引用...恩,就是引用。 因为引用令独立的标量代表大量的数据, 所以我们不应该对把引用用于所有对象 感到奇怪。 方法调用: 不管使用哪种方法调用的形式,Perl 总是会给 构成这个方法的子过程传递一个额外的初始化参数。 如果用一个类调用该方法, 那个参数将 会是类的名字。如果用一个对象调用方法,那个参数就是对象的引用。 OO 的核心是下面这样简单的逻辑:一旦得知调用者,则可以 知道调用者的类,而一旦知道了类,就知道了类的继承关系,一旦知道了类的继承关系,那 么就知道实际调用的子过程了。 使用箭头操作符的方法调用: 我们说过有两种风格的方法调用。第一种调用方法的风格看起来象下面这样: INVOCANT->METHOD(LIST) INVOCANT->METHOD 当 INVOCANT 是一个 包的名字的时候,我们把那个被调用的 METHOD 看作类方法。 比如,使用类方法 summon 的构造一个类,然后在生成的对象上调用实例方法 speak, 你可以这么说: $mage = Wizard->summon("Gandalf"); # 类方法 类方法的参数是类 Wizard $mage->speak("friend"); # 实例方法 使用间接对象的方法调用: 间接对象的句法障碍: 引用包的类: 构造对象 可继承构造器: 和所有方法一样, 构造器只是一个子过程, 但是我们不把它看作一个子过程。 在这个例子里, 我们总是把它当作一个方法来调用——一个类方法, 因为调用者是一个包名字。 [root@wx03 5]# cat hui.pm package hui; sub new { my $class=shift; my $self = { }; bless ($self, $class); return $self; }; sub spawn { my $invocant = shift; print "$invocant is $invocant "; my $class = ref($invocant) || $invocant; # 对象或者类名字 如果是对象 取类的名字 print "$class is $class "; my $self = { }; bless ($self, $class); return $self; }; 1; [root@wx03 5]# cat a2.pl use hui; $ua=hui->new(); print $ua; print " "; $ua->spawn; [root@wx03 5]# perl a2.pl hui=HASH(0xd8d160) $invocant is hui=HASH(0xd8d160) $class is hui 初始器: sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { @_ }; # 剩下的参数变成属性 bless($self, $class); # 给予对象性质 return $self; } 更灵活一些,你可以用缺省键字/数值对设置你的构造器,这些参数可以由用户在使用的时 候通过提供参数而覆盖掉: [root@wx03 5]# cat Horse.pm package Horse; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { color => "bay", legs => 4, owner => undef, @_, # 覆盖以前的属性 }; return bless $self, $class; }; 1; [root@wx03 5]# cat a3.pl use Horse; $ed = Horse->new; use Data::Dumper; $str=Dumper($ed); print "$str is $str "; $stallion = Horse->new(color => "black"); # 四腿黑马 $str=Dumper($stallion); print "$str is $str "; [root@wx03 5]# perl a3.pl $str is $VAR1 = bless( { 'owner' => undef, 'legs' => 4, 'color' => 'bay' }, 'Horse' ); $str is $VAR1 = bless( { 'owner' => undef, 'legs' => 4, 'color' => 'black' }, 'Horse' ); 当把这个 Horse 构造器当作实例方法使用的时候,它忽略它的调用者现有的属性。 任何碰巧创建和返回一个对象的方法都是实际上的构造器 perl 把对象转换成散列: [root@wx03 5]# cat a4.pl use Horse; use Data::Dumper; $steed = Horse->new(color => "dun"); $str=Dumper($steed); print "$str is $str "; print "111111111111 "; print %$steed; print " "; print "2222222222 "; %hash=%$steed; print $hash{color}; print " "; [root@wx03 5]# perl a4.pl $str is $VAR1 = bless( { 'legs' => 4, 'owner' => undef, 'color' => 'dun' }, 'Horse' ); 111111111111 legs4ownercolordun 2222222222 dun //********** sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { color => "bay", legs => 4, owner => undef, @_, # 覆盖以前的属性 }; return bless $self, $class; } sub clone { my $model = shift; my $self = $model->new(%$model, @_); ##把$model对象转成hash,@_ 是传入的参数 这里是owner => "EquuGen Guild, Ltd." #$model 作为new的第一个参数,会提取类名 return $self; # 前面被 ->new 赐福过了 } 类继承: Perl 是这样实现继承的:一个包的@ISA 数组里的每个元素都保留另外一个包的名字, 当缺失方法的时候就搜索这些包。 比如,下面的代码把Horse类变成Critter类的子类。 (我们用our声明@ISA,因为它必须是一个包变量,而不能是my声明的词法作用域变量)。 package Horse; our @ISA = "Critter"; 你现在应该可以在原先Critter使用的任何地方使用Horse类或者对象了。 如果你的新类通过了这样的空子类测试,那么你就可以认为Critter是一个正确的基类, 可以用于继承。 [root@wx03 5]# cat Critter.pm package Critter; sub new { my $self = {}; my $invocant = shift; my $class = ref($invocant) || $invocant; my ($name)=@_; my $self = { "name" =>$name }; bless $self, $class; # Use class name to bless() reference return $self; }; sub sum2 { $self=shift; my $a=shift; my $b=shift; return $a + $b; }; sub fun1 { $self=shift; my $a=shift; my $b=shift; return $a / $b; } 1; [root@wx03 5]# cat a4.pl use Horse; use Data::Dumper; $steed = Horse->new(color => "dun"); print $steed->fun1(10,2); print " "; [root@wx03 5]# perl a4.pl 5 你现在应该可以在原先 Critter 使用的任何地方使用 Horse 类或者对象了。