PERL 中的面向对象编程
我们已经研究过 Perl 和 Perl 匿名数组和散列中的引用。 Perl 中的面向对象概念非常基于引用、匿名数组和散列。 让我们开始学习面向对象 Perl 的基本概念。
对象基础
从 Perl 如何处理对象的角度来解释三个主要术语。 这些术语是对象、类和方法。
Perl 中的对象 仅仅是对知道它属于哪个类的数据类型的引用。 该对象作为引用存储在标量变量中。 因为标量只包含对对象的引用,所以同一个标量可以在不同的类中保存不同的对象。
Perl 中的类 是一个包,其中包含创建和操作对象所需的相应方法。
Perl 中的方法 是一个子例程,由包定义。 该方法的第一个参数是对象引用或包名,具体取决于该方法是影响当前对象还是类。
Perl 提供了一个 bless() 函数,用于返回一个最终成为对象的引用。
定义一个类
在 Perl 中定义一个类非常简单。 一个类以最简单的形式对应于一个 Perl 包。 要在 Perl 中创建一个类,我们首先要构建一个包。
包是用户定义的变量和子程序的自包含单元,可以反复重复使用。
Perl 包在 Perl 程序中提供了一个单独的命名空间,它使子例程和变量保持独立,不会与其他包中的内容发生冲突。
要在 Perl 中声明一个名为 Person 的类,我们这样做 −
package Person;
包定义的范围扩展到文件的末尾,或者直到遇到另一个包关键字。
创建和使用对象
要创建一个类(一个对象)的实例,我们需要一个对象构造函数。 此构造函数是在包中定义的方法。 大多数程序员选择将此对象构造方法命名为 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; }
定义方法
其他面向对象的语言具有数据安全的概念,以防止程序员直接更改对象数据,并提供访问器方法来修改对象数据。 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 步中没有找到任何方法,那么如果在 @ISA 树中找到了一个 AUTOLOAD 子例程,那么 Perl 将使用一个 AUTOLOAD 子例程。
如果仍然找不到匹配的方法,则 Perl 会在作为标准 Perl 库一部分的 UNIVERSAL 类(包)中搜索该方法。
如果该方法仍然没有找到,那么 Perl 放弃并引发一个运行时异常。
因此,要创建一个从我们的 Person 类继承方法和属性的新 Employee 类,我们只需编写如下代码: 将此代码保存到 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 的成员函数(子例程),在以下情况下会自动调用 −
- 当对象引用的变量超出范围时。
- 当对象引用的变量未定义时。
- 脚本终止时
- 当 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