第十二章 对象(上): 12.1 简单复习一下面向对象的语言: 对象是一个数据结构,带有一些行为。我们通常把这些行为称为对象的直接动作,有时候 可以把这些对象拟人化。 作为一个类的实例,对象从中获益,取得其行为。类定义方法:就是那些应用于类和它的事例的性质。 如果需要区分上面两张情况: 1.那么我们就把适用于某一特定对象的方法叫做实例方法 2.而把那些适用于整个类的方法叫做类方法。 不过这样做只是为了方便---对于Perl而言,方法就是方法,只是由其第一个参数的类型来区分。 你可以把实例方法看作一个由特定对象执行的某种动作 类里面那些生成实例的方法叫构造方法。 一个类可以从父类中继承方法,父类也叫基类或者超级类。如果类是这样生成的,那么它叫派生类或者子类 [root@node01 ~]# cat Pkg01.pm package Pkg01; sub fun1 { my $self=shift; my $a=shift; my $b=shift; print $a * $b +3;}; 1; [root@node01 ~]# cat Pkg02.pm package Pkg02; sub fun2 { my $self=shift; my $a=shift; my $b=shift; print $a * $b +4;}; 1; [root@node01 ~]# cat Pkg03.pm package Pkg03; sub fun3 { my $self=shift; my $a=shift; my $b=shift; print $a * $b + 4;}; 1; [root@node01 ~]# cat Scan.pm package Scan; use base qw(Pkg01 Pkg02 Pkg03); sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { }; bless $self, $class; return $self; }; sub fun4 { my $self=shift; my $a=shift; my $b=shift; print $a + $b; }; 1; [root@node01 ~]# cat p1.pl use Scan; $ua=Scan->new(); $ua->fun1(4,5); print "\n"; $ua->fun2(6,7); print "\n"; $ua->fun3(8,5); print "\n"; $ua->fun4(3,6); print "\n"; [root@node01 ~]# perl p1.pl 23 46 44 9 12.2 Perl 的对象系统 Perl 没有为定义对象,类或者方法提供任何特殊的语法。 相反,它使用现有的构造器来实现这三种概念。 一个对象只不过是一个引用 嗯,就是引用 因为引用令独立的标量代表大量的数据,所以我们不应该把引用用于所有对象 感到奇怪。 一个类只是一个包 12.3 方法调用: 方法是对象系统的核心,意味它们为实现所有魔术提供了抽象层。 你不是直接访问对象里的一块数据区,而是调用一个实例方法, 你不是直接调用某个包里的子过程,而是调用一个类的方法。 不管使用哪种方法调用的形式,Perl总是会给构成这个方法的子过程传递一个额外的初始化参数。 如果用一个类调用该方法,那么参数将会是类的名字 如果用一个对象调用方法,那么参数就是对象的引用 12.3.1 使用箭头操作符的方法调用: 我们说挂有两种风格的方法调用,第一种调用的方法的风格看起来像下面这样: INVOCANT->METHOD(LIST) INVOCANT->METHOD 而当INVOCANT 是一个包的名字的时候,我们把那个调用的METHOD看作类的方法。 比如,使用类方法summon 的构造一个类,然后再生成的对象上调用实例方法speak,你可以这么说: $mage = Wizard->summon("Gandalf"); # 类方法 $mage->speak("friend"); # 实例方法 [root@node01 perl]# cat Wizard.pm package Wizard; sub summon { my $invocant = shift; my $class = ref($invocant) || $invocant; print "\$class==$class\n"; my $self = { }; bless $self, $class; return $self; }; sub speak { my $self=shift; print "freend*5"; }; 1; [root@node01 perl]# cat Wizard.pm package Wizard; sub summon { my $invocant = shift; my $class = ref($invocant) || $invocant; print "\$class==$class\n"; my $self = { }; bless $self, $class; return $self; }; sub speak { my $self=shift; print "freend*5"; }; 1; [root@node01 perl]# cat a2.pl use Wizard; $mage = Wizard->summon("Gandalf"); # 类方法 $mage->speak("friend"); # 实例方法 [root@node01 perl]# perl a2.pl $class==Wizard freend*5[root@node01 perl]# 12.3.2 使用间接对象的方法调用; 第二种风格的方法调用起来像这样: METHOD INVOCANT (LIST) METHOD INVOCANT LIST METHOD INVOCANT 12.3.4 引用包的类; 12.4 构造对象 所有对象都是引用,但不是所有引用都是对象 一个引用不会作为对象运转,除非引用它的东西有特殊标记告诉Perl它属于哪个包。 bless 函数接收一个或者两个参数,第一个参数是引用,而第二个是要把引用赐福(bless)成的包。 如果忽略第二个参数,则使用当前包: [root@node01 perl]# cat Luo.pm package Luo; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; print "\$class==$class\n"; my $obj = { } ; bless $obj, $class; return $obj; }; sub fun1 { my $self=shift; my $a=shift; my $b=shift; return $a + $b; }; 1; [root@node01 perl]# cat a4.pl use Luo; my $ua=Luo->new(); print $ua->fun1(44,33); [root@node01 perl]# perl a4.pl $class==Luo 77[root@node01 perl]# 12.4.1 可继承构造器: 和所有方法一样,构造器只是一个子过程,但是我们不把它看作一个子过程。 在这个例子里,我们总是把它当作一个方法调用---一个类方法,因为调用者是一个包的名字。 方法调用: [root@node01 perl]# cat Luo.pm package Luo; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; print "\$class==$class\n"; my $obj = { } ; bless $obj, $class; return $obj; }; sub fun1 { my $self=shift; my $a=shift; my $b=shift; return $a + $b; }; 1; [root@node01 perl]# cat a4.pl use Luo; my $ua=Luo->new(); print $ua->fun1(44,33); [root@node01 perl]# perl a4.pl $class==Luo $self==Luo=HASH(0x14af160) 77[root@node01 perl]# 子过程调用: [root@node01 perl]# cat a5.pl use Luo; print Luo::fun1(44,33); [root@node01 perl]# perl a5.pl $self==44 33[root@node01 perl]# $type = "Spider"; $shelob = $type->spawn; # 和 "Spider"->spawn 一样 这些仍然是类方法,不是实例方法,因为它的调用者持有的是字串而不是一个引用。 [root@node01 perl]# cat Luo.pm package Luo; sub new { my $invocant = shift; print "\$invocant==$invocant\n"; my $class = ref($invocant) || $invocant; print "\$class==$class\n"; my $obj = { } ; bless $obj, $class; return $obj; }; sub fun1 { my $self=shift; print "\$self==$self\n"; my $a=shift; my $b=shift; return $a + $b; }; 1; [root@node01 perl]# cat a4.pl use Luo; my $ua=Luo->new(); my $xx=$ua->new(); print $ua->fun1(44,33); [root@node01 perl]# perl a4.pl $invocant==Luo $class==Luo $invocant==Luo=HASH(0xdb3160) $class==Luo $self==Luo=HASH(0xdb3160) 77[root@node01 perl]# 12.4.2 初始器: 大多数对象维护的信息是由对象的方法间接操作的,到目前为止我们的所有构造器都创建了空散列, 但是我们没有理由让它们这么空着。 [root@node01 perl]# cat Horse.pm package Horse; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { @_ }; # 剩下的参数变成属性 bless($self, $class); # 给予对象性质 return $self; }; sub fun1 { my $self=shift; print $self->{'name'}."xxxx"."\n";; print $self->{'color'}."yyyy"."\n"; }; 1; [root@node01 perl]# cat a6.pl use Horse; my $ua=Horse->new(name => "shadowfax", color => "white"); $ua->fun1(); [root@node01 perl]# perl a6.pl shadowfaxxxxx whiteyyyy 你可以把你的构造器命名为任意的东西,任何碰巧创建和返回一个对象的方法都是实际上的构造器。 [root@node01 perl]# 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; }; sub fun1 { my $self=shift; print $self->{'name'}."xxxx"."\n";; print $self->{'color'}."yyyy"."\n"; }; 1; [root@node01 perl]# 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; }; sub fun1 { my $self=shift; print $self->{'name'}."xxxx"."\n";; print $self->{'color'}."yyyy"."\n"; }; 1; [root@node01 perl]# cat a6.pl use Horse; my $ua=Horse->new(name => "shadowfax", color => "white"); $ua->fun1(); [root@node01 perl]# perl a6.pl shadowfaxxxxx whiteyyyy 12.5 类继承 对Perl的对象系统剩下的内容而言,从一个类继承另外一个类并不需要给这门语言增加特殊的语法。 当你调用一个方法的时候,如果Perl在调用者的包里找不到这个子过程, packge Mule; use base ("Horse", "donkey"); # 声明一个超类 它是下面东西的缩写: package Mule; BEGIN { our @ISA = ("Horse", "Donkey"); require Horse; require Donkey; } /*************************************************** [root@node01 zhao]# cat Mule.pm package Mule; use base ("Horse", "donkey"); sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { @_ }; # 剩下的参数变成属性 bless($self, $class); # 给予对象性质 return $self; }; sub fun3 { my $self=shift; my $a=shift; my $b=shift; return $a + $b +33; }; 1; [root@node01 zhao]# cat Horse.pm package Horse; sub fun1 { my $self=shift; my $a=shift; my $b=shift; return $a + $b +11; }; 1 [root@node01 zhao]# cat donkey.pm package donkey; sub fun2 { my $self=shift; my $a=shift; my $b=shift; return $a + $b +22; }; 1 [root@node01 zhao]# cat a1.pl use Mule; my $ua=Mule->new(); print $ua->fun1(11,22); print "\n"; print $ua->fun2(11,22); print "\n"; print $ua->fun3(11,22); print "\n"; [root@node01 zhao]# perl a1.pl 44 55 66 等价于: [root@node01 zhao]# cat Mule.pm package Mule; BEGIN { our @ISA = ("Horse", "donkey"); require Horse; require donkey; }; sub new { my $invocant = shift; my $class = ref($invocant) || $invocant; my $self = { @_ }; # 剩下的参数变成属性 bless($self, $class); # 给予对象性质 return $self; }; sub fun3 { my $self=shift; my $a=shift; my $b=shift; return $a + $b +33; }; 1; [root@node01 zhao]# cat a1.pl use Mule; my $ua=Mule->new(); print $ua->fun1(11,22); print "\n"; print $ua->fun2(11,22); print "\n"; print $ua->fun3(11,22); print "\n"; [root@node01 zhao]# perl a1.pl 44 55 66 package Mule; BEGIN { our @ISA = ("Horse", "donkey"); require Horse; require donkey; }; 表示 Mule 继承了Horse类和donkey类