Perl面向物件程式設計



我們已經學習了Perl中的引用以及Perl匿名陣列和雜湊表。Perl中的面向物件概念很大程度上基於引用、匿名陣列和雜湊表。讓我們開始學習Perl面向物件程式設計的基本概念。

物件基礎

從Perl如何處理物件的角度出發,解釋三個主要術語:物件、類和方法。

  • 在Perl中,**物件**僅僅是對知道自身所屬類的某種資料型別的引用。物件儲存為標量變數中的引用。因為標量只包含對物件的引用,所以同一個標量可以在不同的類中儲存不同的物件。

  • 在Perl中,**類**是一個包含建立和操作物件所需相應方法的包。

  • 在Perl中,**方法**是一個在包內定義的子程式。方法的第一個引數是物件引用或包名,取決於方法是影響當前物件還是類。

Perl提供了一個**bless()**函式,用於返回最終成為物件的引用。

定義類

在Perl中定義類非常簡單。在最簡單的形式下,類對應於Perl包。要在Perl中建立類,我們首先建立一個包。

包是使用者定義變數和子程式的自包含單元,可以反覆重用。

Perl包在Perl程式中提供了一個獨立的名稱空間,使子程式和變數保持獨立,避免與其他包中的那些衝突。

要在Perl中宣告名為Person的類,我們這樣做:

package Person;

包定義的作用域擴充套件到檔案的末尾,或者直到遇到另一個package關鍵字。

建立和使用物件

要建立一個類的例項(一個物件),我們需要一個物件建構函式。這個建構函式是在包內定義的方法。大多數程式設計師選擇將這個物件建構函式方法命名為new,但在Perl中可以使用任何名稱。

你可以在Perl中使用任何型別的Perl變數作為物件。大多數Perl程式設計師選擇陣列或雜湊表的引用。

讓我們使用Perl雜湊引用為Person類建立建構函式。建立物件時,需要提供一個建構函式,它是一個包內的子程式,返回一個物件引用。物件引用是透過將對包的類的引用進行bless建立的。例如:

package Person;
sub new {
   my $class = shift;
   my $self = {
      _firstName => shift,
      _lastName  => shift,
      _ssn       => shift,
   };
   # Print all the values just for clarification.
   print "First Name is $self->{_firstName}\n";
   print "Last Name is $self->{_lastName}\n";
   print "SSN is $self->{_ssn}\n";
   bless $self, $class;
   return $self;
}

現在讓我們看看如何建立物件。

$object = new Person( "Mohammad", "Saleem", 23234345);

如果你不想為任何類變數賦值,可以在建構函式中使用簡單的雜湊表。例如:

package Person;
sub new {
   my $class = shift;
   my $self = {};
   bless $self, $class;
   return $self;
}

定義方法

其他面向物件的語言具有資料安全的概念,以防止程式設計師直接更改物件資料,並且它們提供訪問器方法來修改物件資料。Perl沒有私有變數,但我們仍然可以使用輔助方法的概念來操作物件資料。

讓我們定義一個輔助方法來獲取人的名字:

sub getFirstName {
   return $self->{_firstName};
}

另一個設定人名的輔助函式:

sub setFirstName {
   my ( $self, $firstName ) = @_;
   $self->{_firstName} = $firstName if defined($firstName);
   return $self->{_firstName};
}

現在讓我們看看完整的例子:將Person包和輔助函式放在Person.pm檔案中。

#!/usr/bin/perl 

package Person;

sub new {
   my $class = shift;
   my $self = {
      _firstName => shift,
      _lastName  => shift,
      _ssn       => shift,
   };
   # Print all the values just for clarification.
   print "First Name is $self->{_firstName}\n";
   print "Last Name is $self->{_lastName}\n";
   print "SSN is $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檔案中使用Person物件,如下所示:

#!/usr/bin/perl

use Person;

$object = new Person( "Mohammad", "Saleem", 23234345);
# Get first name which is set using constructor.
$firstName = $object->getFirstName();

print "Before Setting First Name is : $firstName\n";

# Now Set first name using helper function.
$object->setFirstName( "Mohd." );

# Now get first name set by helper function.
$firstName = $object->getFirstName();
print "Before Setting First Name is : $firstName\n";

當我們執行上述程式時,它會產生以下結果:

First Name is Mohammad
Last Name is Saleem
SSN is 23234345
Before Setting First Name is : Mohammad
Before Setting First Name is : Mohd.

繼承

面向物件程式設計有一個非常有用且有用的概念叫做繼承。繼承簡單來說就是父類的屬性和方法將可用於子類。因此,您不必一遍又一遍地編寫相同的程式碼,您可以繼承父類。

例如,我們可以有一個Employee類,它繼承自Person類。這被稱為“isa”關係,因為員工是人。Perl有一個特殊的變數@ISA來輔助此操作。@ISA控制(方法)繼承。

使用繼承時,需要考慮以下重要事項:

  • Perl在指定物件的類中搜索給定的方法或屬性,即變數。

  • Perl搜尋在物件類的@ISA陣列中定義的類。

  • 如果在步驟1或2中找不到方法,則Perl使用AUTOLOAD子程式(如果在@ISA樹中找到)。

  • 如果仍然找不到匹配的方法,則Perl會在標準Perl庫的一部分UNIVERSAL類(包)中搜索該方法。

  • 如果仍然找不到方法,則Perl放棄並引發執行時異常。

因此,要建立一個新的Employee類來繼承Person類的所有方法和屬性,我們只需編寫如下程式碼:將此程式碼放入Employee.pm中。

#!/usr/bin/perl

package Employee;
use Person;
use strict;
our @ISA = qw(Person);    # inherits from Person

現在Employee類繼承了Person類中的所有方法和屬性,您可以按如下方式使用它們:使用main.pl檔案進行測試:

#!/usr/bin/perl

use Employee;

$object = new Employee( "Mohammad", "Saleem", 23234345);
# Get first name which is set using constructor.
$firstName = $object->getFirstName();

print "Before Setting First Name is : $firstName\n";

# Now Set first name using helper function.
$object->setFirstName( "Mohd." );

# Now get first name set by helper function.
$firstName = $object->getFirstName();
print "After Setting First Name is : $firstName\n";

當我們執行上述程式時,它會產生以下結果:

First Name is Mohammad
Last Name is Saleem
SSN is 23234345
Before Setting First Name is : Mohammad
Before Setting First Name is : Mohd.

方法重寫

子類Employee繼承了父類Person的所有方法。但是,如果您想在子類中重寫這些方法,則可以透過提供您自己的實現來做到這一點。您可以在子類中新增其他函式,或者新增或修改父類中現有方法的功能。這可以按如下方式完成:修改Employee.pm檔案。

#!/usr/bin/perl

package Employee;
use Person;
use strict;
our @ISA = qw(Person);    # inherits from Person

# Override constructor
sub new {
   my ($class) = @_;

   # Call the constructor of the parent class, Person.
   my $self = $class->SUPER::new( $_[1], $_[2], $_[3] );
   # Add few more attributes
   $self->{_id}   = undef;
   $self->{_title} = undef;
   bless $self, $class;
   return $self;
}

# Override helper function
sub getFirstName {
   my( $self ) = @_;
   # This is child class function.
   print "This is child class helper function\n";
   return $self->{_firstName};
}

# Add more methods
sub setLastName{
   my ( $self, $lastName ) = @_;
   $self->{_lastName} = $lastName if defined($lastName);
   return $self->{_lastName};
}

sub getLastName {
   my( $self ) = @_;
   return $self->{_lastName};
}

1;

現在讓我們再次嘗試在main.pl檔案中使用Employee物件並執行它。

#!/usr/bin/perl

use Employee;

$object = new Employee( "Mohammad", "Saleem", 23234345);
# Get first name which is set using constructor.
$firstName = $object->getFirstName();

print "Before Setting First Name is : $firstName\n";

# Now Set first name using helper function.
$object->setFirstName( "Mohd." );

# Now get first name set by helper function.
$firstName = $object->getFirstName();
print "After Setting First Name is : $firstName\n";

當我們執行上述程式時,它會產生以下結果:

First Name is Mohammad
Last Name is Saleem
SSN is 23234345
This is child class helper function
Before Setting First Name is : Mohammad
This is child class helper function
After Setting First Name is : Mohd.

預設自動載入

Perl提供了一個在其他程式語言中找不到的功能:預設子程式。這意味著,如果您定義了一個名為**AUTOLOAD()**的函式,則對未定義子程式的所有呼叫將自動呼叫AUTOLOAD()函式。缺失子程式的名稱可在該子程式中作為$AUTOLOAD訪問。

預設自動載入功能對於錯誤處理非常有用。這是一個實現AUTOLOAD的示例,您可以根據自己的方式實現此函式。

sub AUTOLOAD {
   my $self = shift;
   my $type = ref ($self) || croak "$self is not an object";
   my $field = $AUTOLOAD;
   $field =~ s/.*://;
   unless (exists $self->{$field}) {
      croak "$field does not exist in object/class $type";
   }
   if (@_) {
      return $self->($name) = shift;
   } else {
      return $self->($name);
   }
}

解構函式和垃圾收集

如果您以前使用過面向物件程式設計,那麼您將意識到需要建立一個**解構函式**來釋放使用完物件後分配給該物件的記憶體。Perl會在物件超出作用域後自動執行此操作。

如果您想實現解構函式(應負責關閉檔案或執行一些額外的處理),則需要定義一個名為**DESTROY**的特殊方法。此方法將在Perl釋放分配給它的記憶體之前立即在物件上呼叫。在所有其他方面,DESTROY方法與任何其他方法一樣,您可以在此方法中實現任何您想要的邏輯。

解構函式方法只是一個名為DESTROY的成員函式(子程式),它將在以下情況下自動呼叫:

  • 當物件引用的變數超出作用域時。
  • 當物件引用的變數被undef時。
  • 當指令碼終止時
  • 當perl直譯器終止時

例如,您可以簡單地將以下DESTROY方法放入您的類中:

package MyClass;
...
sub DESTROY {
   print "MyClass::DESTROY called\n";
}

Perl面向物件程式設計示例

這是一個很好的例子,它將幫助您理解Perl的面向物件概念。將此原始碼放入任何Perl檔案中並執行它。

#!/usr/bin/perl

# Following is the implementation of simple Class.
package MyClass;

sub new {
   print "MyClass::new called\n";
   my $type = shift;            # The package/type name
   my $self = {};               # Reference to empty hash
   return bless $self, $type;   
}

sub DESTROY {
   print "MyClass::DESTROY called\n";
}

sub MyMethod {
   print "MyClass::MyMethod called!\n";
}


# Following is the implemnetation of Inheritance.
package MySubClass;

@ISA = qw( MyClass );

sub new {
   print "MySubClass::new called\n";
   my $type = shift;            # The package/type name
   my $self = MyClass->new;     # Reference to empty hash
   return bless $self, $type;  
}

sub DESTROY {
   print "MySubClass::DESTROY called\n";
}

sub MyMethod {
   my $self = shift;
   $self->SUPER::MyMethod();
   print "   MySubClass::MyMethod called!\n";
}

# Here is the main program using above classes.
package main;

print "Invoke MyClass method\n";

$myObject = MyClass->new();
$myObject->MyMethod();

print "Invoke MySubClass method\n";

$myObject2 = MySubClass->new();
$myObject2->MyMethod();

print "Create a scoped object\n";
{
   my $myObject2 = MyClass->new();
}
# Destructor is called automatically here

print "Create and undef an object\n";
$myObject3 = MyClass->new();
undef $myObject3;

print "Fall off the end of the script...\n";
# Remaining destructors are called automatically here

當我們執行上述程式時,它會產生以下結果:

Invoke MyClass method
MyClass::new called
MyClass::MyMethod called!
Invoke MySubClass method
MySubClass::new called
MyClass::new called
MyClass::MyMethod called!
MySubClass::MyMethod called!
Create a scoped object
MyClass::new called
MyClass::DESTROY called
Create and undef an object
MyClass::new called
MyClass::DESTROY called
Fall off the end of the script...
MyClass::DESTROY called
MySubClass::DESTROY called
廣告
© . All rights reserved.