搜尋
  • 登入
  • 報名
密碼重置成功

關注您感興趣的項目並了解有關它們的最新消息

Perl 物件導向

收藏 5
閱讀 17409
更新時間 2016-09-11

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 called\n";
}

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 called
MyClass::MyMethod called!
调用 MySubClass 方法
MySubClass::new called
MyClass::new called
MyClass::MyMethod called!
   MySubClass::MyMethod called!
创建一个作用域对象
MyClass::new called
MyClass::DESTROY called
创建对象
MyClass::new called
MyClass::DESTROY called
脚本执行结束...
MyClass::DESTROY called
MySubClass::DESTROY called
熱AI工具
Undress AI Tool
Undress AI Tool

免費脫衣圖片

AI Clothes Remover
AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undresser.AI Undress
Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

Stock Market GPT
Stock Market GPT

人工智慧支援投資研究,做出更明智的決策

熱門工具
記事本++7.3.1
記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版
SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1
禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6
Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版
SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)