Perl 简明教程
Object Oriented Programming in PERL
我们已经学习过 Perl 中的引用以及 Perl 匿名数组和哈希。Perl 中的面向对象的概念在很大程度上基于引用、匿名数组和哈希。让我们开始学习面向对象 Perl 的基本概念。
Object Basics
有三个主要术语,从 Perl 如何处理对象的角度对其进行了解释。这些术语是对象、类和方法。
-
object 在 Perl 中仅仅是对一个数据类型的引用,该数据类型知道它属于哪一类。对象作为引用存储在一个标量变量中。因为一个标量只包含对对象的引用,所以同一个标量可以在不同的类中保存不同的对象。
-
class 在 Perl 中是一个程序包,其中包含创建和操作对象所需相应的方法。
-
method 在 Perl 中是一个子程序,由程序包定义。对该方法的第一个参数是一个对象引用或程序包名称,具体取决于该方法是要影响当前对象还是类。
Perl 提供了一个 bless() 函数,用于返回一个最终成为对象的引用。
Defining a Class
在 Perl 中定义一个类非常简单。一个类对应于它最简单的形式中的一个 Perl 程序包。要在 Perl 中创建一个类,我们首先构造一个程序包。
程序包是一个自包含的、用户定义变量和子程序的单元,可以反复重用。
Perl 程序包提供了 Perl 程序内部的一个独立命名空间,使子程序和变量保持独立,以免与其他程序包中的变量和子程序发生冲突。
要在 Perl 中声明一个名为 Person 的类,我们这样做 −
package Person;
程序包定义的范围扩展到文件末尾,或者直到遇到另一个程序包关键字。
Creating and Using Objects
若要创建类的实例(对象),我们需要一个对象构造函数。此构造函数是在程序包中定义的一个方法。大多数程序员选择将此对象构造函数方法命名为 new,但你可以在 Perl 中使用任何名称。
你可以将任何类型的 Perl 变量作为 Perl 中的对象使用。大多数 Perl 编程人员会选择对数组或哈希的引用。
让我们使用 Perl 哈希引用为 Person 类创建构造函数。在创建对象时,你需要提供一个构造函数,它是一个程序包中的子程序,用于返回一个对象引用。对象引用通过对程序包的类引用进行祝福来创建。例如 −
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;
}
Defining Methods
其他面向对象语言有数据安全性的概念,以防止程序员直接更改对象数据,并且它们提供了访问器方法来修改对象数据。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.
Inheritance
面向对象编程有一个非常好且有用的概念称为继承。继承简单来说就是,父类的属性和方法都可以被子类使用。因此,您不必一遍又一遍地编写相同的代码,您只需继承一个父类即可。
例如,我们可以有一个继承自 Person 的 Employee 类。这称为“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.
Method Overriding
子类 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.
Default Autoloading
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);
}
}
Destructors and Garbage Collection
如果您以前使用过面向对象编程,那么您将意识到在使用完一个对象后需要创建一个 destructor 来释放分配给该对象的内存。Perl 会在对象超出范围后自动为您执行此操作。
如果您想实现析构函数,它应该负责关闭文件或执行一些额外的处理,那么您需要定义一个名为 DESTROY 的特殊方法。该方法将在 Perl 释放分配给它的内存之前在对象上调用。从所有其他方面来看,DESTROY 方法就像任何其他方法一样,您可以在此方法中实现所需的任何逻辑。
析构函数方法只是一个名为 DESTROY 的成员函数(子例程),它将在以下情况下自动调用 -
-
当对象引用的变量超出了范围。
-
当对象引用的变量被未定义。
-
When the script terminates
-
当 Perl 解释器终止
例如,您可以简单地在类中放置以下方法 DESTROY -
package MyClass;
...
sub DESTROY {
print "MyClass::DESTROY called\n";
}
Object Oriented Perl Example
这里还有另一个很好的示例,它将帮助您了解 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