Perl 面向对象
Perl 中的面向对象编程 (OOP)
Section titled “Perl 中的面向对象编程 (OOP)”Perl 支持面向对象编程(Object-Oriented Programming,OOP)。它的 OO 系统灵活,基于包(packages)、引用(references)和一个称为 bless 的特殊函数。虽然 Perl 内建的 OO 系统功能强大,但现代 Perl 通常使用像 Moo、Moose 或 Object::Pad 这样的系统来提供更多特性和语法糖。
Perl 中的核心 OO 概念
Section titled “Perl 中的核心 OO 概念”- 对象(Object): 在 Perl 中,对象就是一个引用(通常是哈希或数组的引用),它被“祝福(bless)”到了一个类中。祝福将引用与包名关联起来,使其成为该类的一个实例。
- 类(Class): 在 Perl 中,类就是一个包(package)。这个包包含了操作该类对象的方法(methods),方法实际上就是包内的子例程(subroutines)。
- 方法(Method): 方法是在类(包)中定义的子例程。方法的第一个参数始终是对象本身(如果通过对象调用,例如
$obj->method())或类名(如果作为类方法调用,例如ClassName->method())。这个第一个参数通常被称为“调用者(invocant)”。 - 祝福(Blessing):
bless函数用于将一个普通引用转换为对象。bless $reference, $className;将$reference与$className关联起来。
使用 package 关键字定义类。方法是这个包内的子例程。
# File: Person.pmpackage Person;use strict;use warnings;
# Constructorsub new { my ($class, %args) = @_; my $self = { firstName => $args{firstName} // 'N/A', # Using defined-or for defaults lastName => $args{lastName} // 'N/A', ssn => $args{ssn} // '000-00-0000', }; bless $self, $class; # Make $self an object of class $class (Person) return $self;}
# Accessor method (getter)sub get_full_name { my ($self) = @_; return $self->{firstName} . " " . $self->{lastName};}
# Mutator method (setter)sub set_ssn { my ($self, $new_ssn) = @_; if (defined $new_ssn) { # Basic validation could go here $self->{ssn} = $new_ssn; } return $self->{ssn};}
1; # Modules must return a true value创建和使用对象(实例化)
Section titled “创建和使用对象(实例化)”要创建类的一个实例(即对象),你需要调用其构造方法,按照惯例通常命名为 new。
#!/usr/bin/env perluse strict;use warnings;use FindBin qw($Bin); # To find the script's directoryuse lib $Bin; # Add it to @INC to find Person.pm
use Person; # Load the Person class
# Create a Person objectmy $person1 = Person->new( firstName => "Jane", lastName => "Doe", ssn => "123-45-6789");
# Call methods on the objectprint "Person 1: ", $person1->get_full_name(), "\n";print "Person 1 SSN (initial): ", $person1->{ssn}, "\n"; # Direct access (discouraged for encapsulation)
$person1->set_ssn("987-65-4321");print "Person 1 SSN (updated): ", $person1->{ssn}, "\n";
my $person2 = Person->new(firstName => "John");print "Person 2: ", $person2->get_full_name(), " (SSN: ", $person2->{ssn}, ")\n";脚本输出:
Person 1: Jane DoePerson 1 SSN (initial): 123-45-6789Person 1 SSN (updated): 987-65-4321Person 2: John N/A (SSN: 000-00-0000)封装和访问器方法
Section titled “封装和访问器方法”Perl 的基本 OO 系统不强制执行数据隐私(例如,私有属性)。对象属性(在我们示例中是哈希键)可以直接访问。然而,最佳实践是使用访问器(getter)和修改器(setter)方法来与对象数据交互。这促进了封装(encapsulation),允许对象内部表示的改变,而不会破坏使用该类的代码。
在 Person.pm 示例中,get_full_name 是一个 getter,set_ssn 是一个 setter。使用前导下划线命名属性(例如 _firstName)是一种常见的约定,表示它们 intended for internal use,但 Perl 不强制执行此约定。
继承允许一个类(子类,subclass 或 child class)继承另一个类(超类,superclass 或 parent class)的方法和属性。Perl 通过特殊的包数组 @ISA 实现继承。
要让类 Employee 继承自 Person,需要设置 Employee 的 @ISA 数组:
# File: Employee.pmpackage Employee;use strict;use warnings;use Person; # We need the Person class definition
our @ISA = qw(Person); # Employee ISA Person (inherits from Person)
# Constructor for Employeesub new { my ($class, %args) = @_; # Call parent's constructor my $self = $class->SUPER::new( firstName => $args{firstName}, lastName => $args{lastName}, ssn => $args{ssn} ); # Add Employee-specific attributes $self->{employeeID} = $args{employeeID} // 'EMP-0000'; $self->{department} = $args{department} // 'Unassigned'; # Re-bless into the current class, though SUPER::new already blessed into Person. # For simple inheritance, this bless might not be strictly necessary if SUPER::new # handles the class correctly, but often good for clarity or complex hierarchies. bless $self, $class; return $self;}
# Employee-specific methodsub get_employee_details { my ($self) = @_; return "ID: " . $self->{employeeID} . ", Name: " . $self->get_full_name() . ", Dept: " . $self->{department};}
# Method Overriding: Provide a specialized version of a parent methodsub get_full_name { my ($self) = @_; # Call parent's get_full_name and append title my $fullName = $self->SUPER::get_full_name(); return "Emp. " . $fullName;}
1;使用 Employee 类:
#!/usr/bin/env perluse strict;use warnings;use FindBin qw($Bin);use lib $Bin;
use Employee;
my $emp1 = Employee->new( firstName => "Alice", lastName => "Smith", ssn => "111-22-3333", employeeID => "EMP-101", department => "Engineering");
print $emp1->get_employee_details(), "\n";print "Full name (overridden): ", $emp1->get_full_name(), "\n";
# Calling an inherited method (set_ssn is from Person)$emp1->set_ssn("444-55-6666");print "Updated SSN: ", $emp1->{ssn}, "\n";输出:
ID: EMP-101, Name: Emp. Alice Smith, Dept: EngineeringFull name (overridden): Emp. Alice SmithUpdated SSN: 444-55-6666- Perl 首先在对象的自身类中查找方法。
- 如果未找到,它会按照
@ISA中列出的类进行查找,遵循深度优先、从左到右的顺序。 SUPER::伪类(pseudo-class)允许子类调用其超类中被重写(override)的方法。
方法重写(Method Overriding)
Section titled “方法重写(Method Overriding)”子类可以提供其超类中已定义的方法的自己的实现。这称为方法重写或方法覆盖。上面示例中的 Employee 类重写了 Person 类中的 get_full_name 方法。
AUTOLOAD 方法
Section titled “AUTOLOAD 方法”如果在对象(或类)上调用的方法在其类或任何超类中不存在,Perl 会查找一个名为 AUTOLOAD 的特殊方法。如果定义了 AUTOLOAD,则会使用原始参数调用它。缺失方法的完整限定名称(fully qualified name)可以在包变量 $AUTOLOAD 中获得。
虽然 AUTOLOAD 可用于在运行时创建方法等任务,但它会使代码更难调试和理解。请谨慎使用。
# Inside a class definition# sub AUTOLOAD {# my $self = shift;# my $method_name = $AUTOLOAD;# $method_name =~ s/.*:://; # Strip package name# warn "Called undefined method: $method_name\n";# # ... try to handle it ...# }析构方法和垃圾回收
Section titled “析构方法和垃圾回收”Perl 使用基于引用计数(reference counting)的自动垃圾回收(garbage collection)。当一个对象不再被引用(例如,其变量超出作用域且没有其他引用指向它),Perl 会回收其内存。
如果你需要在对象即将被销毁时执行特定的清理操作(例如,关闭文件句柄、释放资源),可以定义一个名为 DESTROY 的特殊方法。这个析构方法(destructor method)会在对象内存被回收前自动由 Perl 调用。
# Inside a class definition (e.g., Person.pm)# sub DESTROY {# my ($self) = @_;# print "Person object '", $self->get_full_name(), "' is being destroyed.\n";# # ... perform cleanup actions ...# }## # Example of when DESTROY is called:# {# my $scoped_person = Person->new(firstName => "Temp");# # $scoped_person's DESTROY will be called when it goes out of scope here# }## my $global_person = Person->new(firstName => "Global");# undef $global_person; # DESTROY called here# # Or DESTROY called when script ends if $global_person still exists现代 Perl OO 系统
Section titled “现代 Perl OO 系统”虽然理解 Perl 内建的 OO 系统很重要,但许多现代 Perl 项目使用 CPAN 提供的 OO 系统,例如:
- Moo: 轻量、快速且现代的 OO 系统,提供了属性、类型约束和角色等特性,模板代码量极少。
- Moose: 一个高度全面、后现代的 OO 系统,具有丰富的特性,包括角色、类型系统、内省(introspection)和扩展生态系统。它非常强大,但启动开销可能更高。
- Object::Pad: 一个较新的系统(需要 Perl 5.30+),旨在通过关键字插件在 Perl 核心语法中直接提供更传统的 OO 语法(例如
class、has、method关键字)和更好的封装。
这些系统解决了 Perl 基本 OO 的一些冗余和限制,使 OO 开发更健壮和易于维护。