在學(xué)習(xí)了Perl的基本語法之后,學(xué)習(xí)Perl的OOP,略有心得。不知道Perl各個版本之間OOP是否有區(qū)別,但是我是學(xué)習(xí)的Perl5,所以在標題上將版本號也寫出來了。因為了解到PHP4和PHP5的OOP部分就有不小的差別,所以有此擔(dān)心。
學(xué)習(xí)Perl的OOP,最關(guān)鍵的兩件事情就是package和bless。只要把這兩個東西搞清楚也就學(xué)會大一半了。
Perl的package
感覺Perl的package和Java還真有點相似。Java的package是以CLASSPATH中的目錄為根,按目錄定義和搜索分級包名。Perl也類似,是以@INC數(shù)組中的目錄為根,按目錄搜索分級包名。不過有一點不同,Perl的package定義貌似不需要與目錄結(jié)構(gòu)對應(yīng)。具體是什么樣的規(guī)則我沒有去研究,因為按目錄結(jié)構(gòu)定義package是個好習(xí)慣。
相較于Java,Perl的package還有一點很有意思。Java的每層package對應(yīng)一個目錄,而最后是一個class文件對應(yīng)到類名。Perl卻簡化了,package直接就把目錄和文件名都引用了進去。比如
Java中,name.jamesfancy.MyClass,對應(yīng)的是/name/jamesfancy/MyClass.class,源代碼中則分成兩句來寫
復(fù)制代碼 代碼如下:
package name.jamesfancy;
class MyClass {....}
package name.jamesfancy;
class MyClass {....}
Perl中,name::jamesfancy::MyClass,應(yīng)對的是/name/jamesfancy/MyClass.pm,源代碼中只有一句package就說明了
復(fù)制代碼 代碼如下:
package name::jamesfancy::MyClass;
package name::jamesfancy::MyClass;
至于package中的內(nèi)容,也就是變量和子程序,至于區(qū)別,稍后再說。
bless函數(shù)
bless是用來把一個類綁定到引用類型變量的函數(shù)。很奇怪Perl為什么要用這個單詞,不過沒關(guān)系,我們可以把它想像得形象一點:就像游戲里牧師通過祝福技能為某人加上BUFF一樣,bless把一個類綁定到某個引用類型的變量,從此這個變量就受到了祝福,擁有了這個類中的變量和子程序。
bless的用法通常是:bless($引用變量, 類名);
引用變量貌似可以是任何引用類型的變量,我嘗試過Scalar,Array和Hash的引用,都能成功。在bless之外,這個引用變量就可以被稱之為對象了,當然它仍然是個引用,是對象的引用。
有一點還需要注意,雖然這個對象擁有了類的變量和子程序,但我們應(yīng)該把它擁有的類的變量和子程序都看成是靜態(tài)的,換句話說,就是類的成員。在這一點上,子程序的處理會比較特殊一點,但至少類的變量,也就是包變量,是不屬于對象的。因此,所有對象的數(shù)據(jù)都保存在對象引用的原始數(shù)據(jù)中。既然大家都習(xí)慣對象數(shù)據(jù)以鍵值對的方式保存,所以通常情況下,bless的引用變量,都是Hash的引用了。
很抽象么?舉個例子。如果對OOP的成員函數(shù)還不夠了解,那就只看下面示例中每個類的test函數(shù)中第一句以后的內(nèi)容不好。
復(fù)制代碼 代碼如下:
# test.pl
package TestScalar;
sub test {
my $this = shift();
print("\nIn TestScalar::test()\n");
print("Scalar:\n ${$this}\n");
}
package TestArray;
sub test {
my $this = shift();
print("\nIn TestArray::test()\n");
print("Array:\n");
foreach my $item (@{$this}) {
print(" $item\n");
}
}
package TestHash;
sub test {
my $this = shift();
print("\nIn TestHash::test()\n");
print("Hash:\n");
while (my ($key, $value) = each %{$this}) {
printf(" %-4s = %s\n", $key, $value);
}
}
package main;
my $name = "James Fancy";
my $objScalar = \$name;
my $objArray = ['James', 'Fancy', 'Jenny'];
my $objHash = {'name' => 'James', 'age' => 30};
bless($objScalar, 'TestScalar');
bless($objArray, 'TestArray');
bless($objHash, 'TestHash');
$objScalar->test();
$objArray->test();
$objHash->test();
__END__
In TestScalar::test()
Scalar:
James Fancy
In TestArray::test()
Array:
James
Fancy
Jenny
In TestHash::test()
Hash:
name = James
age = 30
從上面的示例中可以看到,分別將3種類型的引用轉(zhuǎn)變?yōu)閷ο蟆V砸杨悓懗?個而非1個,主要是為了在Test里輸出不同類型的數(shù)據(jù)。
類和對象的成員函數(shù)
成員函數(shù)就是在package中定義的子程序。成員函數(shù)是沒有靜態(tài)和非靜態(tài)之分的,但我寧愿大家都把它看作是靜態(tài)函數(shù),因為雖然它即可以當作類成員函數(shù)來調(diào)用,也可以當用對象成員函數(shù)來調(diào)用,但在當作對象成員函數(shù)來調(diào)用的時候,Perl偷偷的傳入了對象引用。這也解釋了為什么通常成員函數(shù)里的第一句話往往是
復(fù)制代碼 代碼如下:
my $this = shift();
當然,這里的$this只是一個局部變量,而不是關(guān)鍵字,你也可以用別的名稱來代替它。比如很多人就喜歡用$self,或者$me等。
假如,對于一個成員函數(shù),分別用類和對象來對它進行調(diào)用,會有什么不一樣呢?再看一個示例:
復(fù)制代碼 代碼如下:
# test.pl
package MyClass;
sub test {
my ($this, @args) = @_;
print('-' x 40, "\n");
print("\$this is [$this], Ref of \$this is [", ref($this), "]\n");
print("Args: [@args]\n");
}
package main;
$obj = {};
bless($obj, 'MyClass');
MyClass->test("MyClass->test(...)");
$obj->test("\$obj->test(...)");
__END__
----------------------------------------
$this is [MyClass], Ref of $this is []
Args: [MyClass->test(...)]
----------------------------------------
$this is [MyClass=HASH(0x178a44)], Ref of $this is [MyClass]
Args: [$obj->test(...)]
從結(jié)果可以看出來,不管哪種方法調(diào)用,第一個參數(shù)都是Perl偷偷傳遞進去的。如果是類調(diào)用,則第一個參數(shù)是該類。如果是對象調(diào)用,第一個參數(shù)是該對象。因此,只需要將ref($this)的結(jié)果和類名進行比較就清楚是哪種調(diào)用了。所以,一個容錯性較好的成員函數(shù),一開始要判斷傳入的第一個參數(shù),比如
復(fù)制代碼 代碼如下:
sub foo {
my $this = shift();
return unless ($this ne 'MyClass');
# 其它語句
}
這里還有一個疑問:既然package中定義的子程序都是成員函數(shù),那不是類的package和是類的package有啥區(qū)別?它們在結(jié)構(gòu)上沒有一點區(qū)別,唯一的區(qū)別在處理中。在調(diào)用子程序的時候,Perl不會硬塞一個類或者對象在參數(shù)列表的最前面,但調(diào)用成員函數(shù)的時候會,所以區(qū)別是根據(jù)你的調(diào)用方式來區(qū)分的。
調(diào)用對象成員還好說,$obj->foo()就好,但是調(diào)用類成員的時候,怎么知道是調(diào)用的類成員還是包中的子程序呢?那就要看是通過“->”還是“::”來調(diào)用的了。下面的例子可以幫助理解:
復(fù)制代碼 代碼如下:
# test.pl
package MyClass;
use Data::Dumper;
sub test {
print('-' x 40, "\n");
print(Dumper(@_));
}
package main;
MyClass->test("MyClass->test(...)");
MyClass::test("MyClass::test(...)");
__END__
----------------------------------------
$VAR1 = 'MyClass';
$VAR2 = 'MyClass->test(...)';
----------------------------------------
$VAR1 = 'MyClass::test(...)';
很明顯,通過“::”調(diào)用的子程序沒有被Perl塞入一個引用類的參數(shù)。
構(gòu)造函數(shù)
Perl的OOP沒有指定專門的構(gòu)造函數(shù),所以你可以把任何一個子程序當作構(gòu)造函數(shù),當然,重要的是其中的內(nèi)容。既然腳本通常不是寫給自己一個人看的,所以還是按照大家的習(xí)慣,把構(gòu)造函數(shù)取名為new吧。按照多數(shù)OOP語言的習(xí)慣,new函數(shù)通常返回一個對象或其引用、指針。所以在Perl中,這個new函數(shù)要返回一個對象引用,理所當然地,把bless動作包含在new函數(shù)中是個好習(xí)慣。那么一個簡單的new函數(shù)看起來就像這樣:
復(fù)制代碼 代碼如下:
sub new {
my $this = {};
bless($this);
}
這個new函數(shù)中產(chǎn)生了一個Hash引用,bless它,并返回它。如果你疑惑為什么這里沒有看到return語句,那么建議你去看看關(guān)于子程序中返回值的資料,順便查一下bless函數(shù)的說明。來看看完整的程序了解一下是怎么使用new函數(shù)的。
12下一頁閱讀全文
您可能感興趣的文章:- PERL腳本 學(xué)習(xí)筆記
- perl腳本實現(xiàn)限制ssh最大登錄次數(shù)(支持白名單)
- Perl使用Tesseract-OCR實現(xiàn)驗證碼識別教程
- perl與shell獲取昨天、明天或多天前的日期的代碼
- 為Java程序員準備的10分鐘Perl教程
- perl批量查詢ip歸屬地的方法代碼
- perl去除重復(fù)內(nèi)容的腳本代碼(重復(fù)行+數(shù)組重復(fù)字段)
- 在Perl中使用Getopt::Long模塊來接收用戶命令行參數(shù)
- perl中單行注釋和多行注釋使用介紹
- perl腳本學(xué)習(xí)指南--讀書筆記