Perl 中有兩種不同地物件導向程式設計的實作:
一是基於匿名雜湊表的方式,每個物件實例的實質就是一個指向匿名雜湊表的參考。在這個匿名哈希表中,儲存來所有的實例屬性。
二是基於數組的方式,在定義一個類別的時候,我們將為每一個實例屬性建立一個數組,而每個物件實例的實質就是一個指向這些數組中某一行索引的引用。在這些數組中,儲存著所有的實例屬性。
物件導向基礎概念
物件導向有很多基礎概念,這裡我們接收三個:物件、類別和方法。
物件:物件是對類別中資料項的參考。 .
類別:類別是個Perl包,其中含提供物件方法的類別。
方法:方法是個Perl子程序,類別名稱是其第一個參數。
Perl 提供了bless() 函數,bless 是用來建構物件的, 透過bless 把一個引用和這個類別名稱相關聯,回傳這個引用就建構出一個物件。
類別的定義
一個類別只是一個簡單的包。
可以把一個包當作一個類別用,並且把包裡的函數當作類別的方法來用。
Perl 的套件提供了獨立的命名空間,所以不同套件的方法與變數名稱不會衝突。
Perl 類別的檔案後綴為.pm。
接下來我們建立一個Person 類別:
package Person;
類別的程式碼範圍到腳本檔案的最後一行,或是到下一個package 關鍵字前。
建立和使用對象
建立一個類別的實例(物件) 我們需要定義一個建構函數,大多數程式使用類別名稱作為建構函數,Perl 中可以使用任何名字。
你可以使用多種Perl 的變數作為Perl 的物件。大多數情況下我們會使用引用數組或哈希。
接下來我們為Person 類別建立一個建構函數,使用了Perl 的雜湊引用。
在創建物件時,你需要提供一個建構函數,它是一個子程序,傳回物件的參考。
實例如下:
實例
package Person ; sub new { my $class = shift ; my $self = { _firstName => shift , _lastName => shift , _ssn => shift , } ; # 輸出使用者資訊 print "名字: $self ->{_firstName} n " ; print "姓氏: $self ->{_lastName} n " ; print "編號: $self ->{_ssn} n " ; bless $self , $class ; return $self ; }接下來我們建立一個物件:
$object = new Person( "小明", "王", 23234345);
定義方法
Perl類別的方法只不過是個Perl子程式而已,也也就是通常所說的成員函數。
Perl物件導向中Perl的方法定義不提供任何特別語法,但規定方法的第一個參數為物件或其被引用的套件。
Perl 沒有提供私有變量,但我們可以透過輔助的方式來管理物件資料。
接下來我們定義一個取得名字的方法:
sub getFirstName { return $self->{_firstName};}同樣也可以這麼寫:
sub setFirstName { my ( $self, $firstName ) = @_; $self->{_firstName} = $firstName if defined($firstName); return $self->{_firstName};}接下來我們修改Person.pm 檔案的程式碼,如下所示:
實例
#!/usr/bin/perl package Person ; sub new { my $class = shift ; my $self = { _firstName => shift , _lastName => shift , _ssn => shift , } ; # 輸出使用者資訊 print "名字: $self ->{_firstName} n " ; print "姓氏: $self ->{_lastName} n " ; print "編號: $self ->{_ssn} n " ; bless $self , $class ; return $self ; } sub setFirstName { my ( $self , $firstName ) = @_ ; $self -> { _firstName } = $firstName if defined ( $firstName ) ; return $self -> { _firstName } ; } sub getFirstName { my ( $self ) = @_ ; return $self -> { _firstName } ; } 1 ; employee.pl 腳本程式碼如下:
實例
#!/usr/bin/perl use Person ; $object = new Person ( "小明" , "王" , 23234345 ) ; # 取得姓名$firstName = $object -> getFirstName ( ) ; print "設定前姓名為: $firstName n " ; # 使用輔助函數設定姓名$object -> setFirstName ( "小強" ) ; # 透過輔助函數取得姓名$firstName = $object -> getFirstName ( ) ; print "設定後姓名為: $firstName n " ;執行以上程序後,輸出結果為:
$ perl employee.pl名字:小明姓氏:王號:23234345設定前姓名為: 小明設定後姓名為: 小強
繼承
Perl 裡類別方法透過@ISA陣列繼承,這個陣列裡麵包含其他套件(類別)的名字,變數的繼承必須明確設定。
多繼承就是這個@ISA數組包含多個類別(包)名字。
透過@ISA只能繼承方法,不能繼承資料。
接下來我們建立一個Employee 類別繼承Person 類別。
Employee.pm 檔案程式碼如下所示:
實例
#!/usr/bin/perl package Employee ; use Person ; use strict ; our @ISA = qw( Person ) ; # 從Person 繼承現在Employee 類別包含了Person 類別的所有方法和屬性,我們在main.pl 檔案中輸入以下程式碼,並執行:
實例
#!/usr/bin/perl use Employee ; $object = new Employee ( "小明" , "王" , 23234345 ) ; # 取得姓名$firstName = $object -> getFirstName ( ) ; print "設定前姓名為: $firstName n " ; # 使用輔助函數設定姓名$object -> setFirstName ( "小強" ) ; # 透過輔助函數取得姓名$firstName = $object -> getFirstName ( ) ; print "設定後姓名為: $firstName n " ;執行以上程序後,輸出結果為:
$ perl main.pl名字:小明姓氏:王編號:23234345設定前姓名為: 小明設定後姓名為: 小強
方法重寫
在上述實例中,Employee 類別繼承了Person 類,但如果Person 類別的方法無法滿足需求,就需要對其方法進行重寫。
接下來我們在Employee 類別中加入一些新方法,並重寫了Person 類別的方法:
實例
#!/usr/bin/perl package Employee ; use Person ; use strict ; our @ISA = qw( Person ) ; # 從Person 繼承 # 重寫建構函數sub new { my ( $class ) = @_ ; # 呼叫父類別的建構子 my $self = $class -> SUPER :: new ( $_ [ 1 ] , $_ [ 2 ] , $_ [ 3 ] ) ; # 新增更多屬性 $self -> { _id } = undef ; $self -> { _title } = undef ; bless $self , $class ; return $self ; } # 重寫方法sub getFirstName { my ( $self ) = @_ ; # 這是子類別函數 print "這是子類別函數n " ; return $self -> { _firstName } ; } # 新增方法sub setLastName { my ( $self , $lastName ) = @_ ; $self -> { _lastName } = $lastName if defined ( $lastName ) ; return $self -> { _lastName } ; } sub getLastName { my ( $self ) = @_ ; return $self -> { _lastName } ; } 1 ;我們在main.pl 檔案中輸入以下程式碼,並執行:
實例
#!/usr/bin/perl use Employee ; $object = new Employee ( "小明" , "王" , 23234345 ) ; # 取得姓名,使用修改後的建構函數$firstName = $object -> getFirstName ( ) ; print "設定前姓名為: $firstName n " ; # 使用輔助函數設定姓名$object -> setFirstName ( "小強" ) ; # 透過輔助函數取得姓名$firstName = $object -> getFirstName ( ) ; print "設定後姓名為: $firstName n " ;執行以上程序後,輸出結果為:
$ perl main.pl名字:小明姓氏:王編號:23234345這是子類別函數設定前姓名為: 小明這是子類別函數設定後姓名為: 小強
預設載入
如果在目前類別、目前類別所有的基底類別、還有UNIVERSAL 類別中都找不到要求的方法,這時會再次尋找名為AUTOLOAD() 的一個方法。如果找到了AUTOLOAD,那麼就會調用,同時設定全域變數$AUTOLOAD 的值為缺少的方法的全限定名稱。
如果還不行,那麼Perl 就宣告失敗並出錯。
如果你不想繼承基底類別的AUTOLOAD,很簡單,只需要一句:
sub AUTOLOAD;
析構函數及垃圾回收
當物件的最後一個引用釋放時,物件會自動析構。
如果你想在析構的時候做些什麼,那麼你可以在類別中定義一個名為"DESTROY"的方法。它將在適合的時機自動調用,並且按照你的意思執行額外的清理動作。
package MyClass;...sub DESTROY{ print "MyClass::DESTROY calledn";} Perl 會把物件的引用當作唯一的參數傳遞給DESTROY。注意這個引用是唯讀的,也就是說你不能透過存取$_[0] 來修改它。 (譯者註:參見perlsub)但是對象本身(如"${$_[0]"或"@{$_[0]}" 還有"%{$_[0]}" 等等)還是可寫的。
如果你在析構器回傳之前重新bless 了物件引用,那麼Perl 會在析構器回傳之後接著呼叫你重新bless 的那個物件的DESTROY 方法。這可以讓你有機會呼叫基底類別或你指定的其它類別的析構器。需要說明的是,DESTROY 也可以手工調用,但是通常沒有必要這麼做。
在目前物件釋放後,包含在目前物件中的其它物件會自動釋放。
Perl 物件導向實例
我們可以透過以下實例來進一步理解Perl物件導向的應用:
實例
#!/usr/bin/perl # 下面是簡單的類別實現package MyClass ; sub new { print " MyClass::new called n " ; my $type = shift ; # 包名 my $self = { } ; # 引用空哈希 return bless $self , $type ; } sub DESTROY { print " MyClass::DESTROY called n " ; } sub MyMethod { print " MyClass::MyMethod called! n " ; } # 繼承實現package MySubClass ; @ISA = qw( MyClass ) ; sub new { print " MySubClass::new called n " ; my $type = shift ; # 包名 my $self = MyClass -> new ; # 引用空哈希 return bless $self , $type ; } sub DESTROY { print " MySubClass::DESTROY called n " ; } sub MyMethod { my $self = shift ; $self -> SUPER :: MyMethod ( ) ; print " MySubClass::MyMethod called! n " ; } # 呼叫以上類別的主程式package main ; print "呼叫MyClass 方法n " ; $myObject = MyClass -> new ( ) ; $myObject -> MyMethod ( ) ; print "呼叫MySubClass 方法n " ; $myObject2 = MySubClass -> new ( ) ; $myObject2 -> MyMethod ( ) ; print "建立一個作用域物件n " ; { my $myObject2 = MyClass -> new ( ) ; } # 自動呼叫析構函數 print "建立物件n " ; $myObject3 = MyClass -> new ( ) ; undef $myObject3 ; print "腳本執行結束... n " ; # 自動執行析構函數執行以上程序,輸出結果為:
呼叫MyClass 方法MyClass::new calledMyClass::MyMethod called!呼叫MySubClass 方法MySubClass::new calledMyClass::new calledMyClass::MyMethod called! MySubClass::MyMethod called!建立一個作用域物件MyClass::new calledMyClass::DESTRSTROY called建立物件MyClass::new calledMyClass::DESTROY called腳本執行結束...MyClass::DESTROY calledMySubClass::DESTROY called