Perl面向对象编程入门与实践
1. 面向对象基础
在Perl中,我们可以使用面向对象的编程方式。例如创建一个
Person
对象:
my $galileo = Person->new(
lastname => "Galilei",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombadier",
);
也可以使用另一种语法来调用构造函数:
my $galileo = new Person (...);
构造函数会检查参数是否可接受,进行必要的转换,创建一个哈希引用,使用
bless()
函数将其与类关联,然后返回该引用。
当对象不再使用时,比如它是一个超出作用域的词法变量,Perl会自动销毁它。在销毁之前,Perl会尝试调用名为
DESTROY()
的方法。如果类提供了这个方法,它应该负责在对象被销毁之前执行任何必要的任务。例如,FTP会话对象会确保关闭与远程服务器的连接。
2. 使用现有类:Net::FTP示例
下面我们通过一个使用
Net::FTP
类的示例来深入了解。这个类允许我们创建对象,用于在FTP服务器和本地之间传输文件。
以下是一个连接到CPAN并下载
README.html
文件的示例代码:
#!/usr/bin/perl
# ftp.pl
use strict;
use Net::FTP;
my $ftp = Net::FTP->new("ftp.cpan.org")
or die "Couldn't connect: $@\n";
$ftp->login("anonymous");
$ftp->cwd("/pub/CPAN");
$ftp->get("README.html");
$ftp->close();
这个程序的执行步骤如下:
1.
use Net::FTP;
:这行代码找到包含
Net::FTP
类定义的文件(名为
FTP.pm
,位于
Net
目录中),并将其编译以供程序使用。
2. 创建对象:
my $ftp = Net::FTP->new("ftp.cpan.org")
or die "Couldn't connect: $@\n";
通过调用构造函数
new()
创建对象,构造函数接受一些参数,如要连接的远程机器等。如果不指定这些参数,构造函数会提供一些合理的默认值。
3. 登录服务器:
$ftp->login("anonymous");
通常从FTP服务器获取文件的方式是使用用户名“anonymous”和电子邮件地址作为密码登录。
login()
方法告诉对象发出相应的登录命令。
4. 更改工作目录并获取文件:
$ftp->cwd("/pub/CPAN");
$ftp->get("README.html");
cwd()
和
get()
是对象提供的方法。
Net::FTP
对象有很多方法,下面是一些常用方法的列表:
| 方法名 | 行为 |
| ---- | ---- |
|
$ftp->login($login,$passwd)
| 使用给定的用户名和密码登录服务器 |
|
$ftp->type($type) $ftp->ascii() $ftp->binary()
| 设置传输类型为ASCII或二进制,类似于Perl的
binmode
操作符 |
|
$ftp->rename($old,$new)
| 重命名文件 |
|
$ftp->delete($file)
| 删除文件 |
|
$ftp->cwd($directory)
| 更改FTP服务器上的工作目录 |
|
$ftp->pwd()
| 获取当前目录的名称 |
|
$ftp->ls()
| 列出FTP服务器上当前目录的内容 |
|
$ftp->get($remote, $local, $offset)
| 从远程服务器获取文件 |
|
$ftp->put($local, $remote)
| 将文件上传到远程服务器 |
- 关闭连接:
$ftp->close();
3. 创建自定义类
我们已经了解了如何使用类和对象,现在来看看如何创建自己的类。以
Person
类为例。
一个类实际上就是一个包,最简单的类如下:
package Person;
但这个类什么都没有,没有方法、属性和构造函数,是一个完全空的类。通常我们会把类放在单独的文件中,例如创建一个
Person1.pm
文件:
package Person1;
# Person1.pm
# Class for storing data about a person
use strict;
1;
在另一个程序中可以使用这个类:
#!/usr/bin/perl
# person1.pl
use warnings;
use strict;
use Person1;
但目前还不能创建对象,因为没有构造函数。
4. bless()函数
bless()
函数可以将一个引用转换为对象。它的语法是:
bless(reference, package);
如果不指定包名,引用将被关联到当前包。以下是一些示例代码:
#!/usr/bin/perl
# reftypes.pl
use warnings;
use strict;
my $a = [];
my $b = {};
my $c = \1;
my $d = \$c;
print '$a is a ', ref($a), " reference\n";
print '$b is a ', ref($b), " reference\n";
print '$c is a ', ref($c), " reference\n";
print '$d is a ', ref($d), " reference\n";
运行结果:
$a is a ARRAY reference
$b is a HASH reference
$c is a SCALAR reference
$d is a REF reference
下面是使用
bless()
函数的示例:
#!/usr/bin/perl
# bless1.pl
use warnings;
use strict;
my $a = {};
print '$a is a ', ref($a), " reference\n";
bless($a, "Person1");
print '$a is a ', ref($a), " reference\n";
运行结果:
$a is a HASH reference
$a is a Person1 reference
虽然引用的类型改变了,但它的内部结构并没有改变,仍然是一个哈希引用。如果再次使用
bless()
函数,引用的类型会再次改变,但通常不建议对已经关联过的对象再次使用
bless()
,因为可能会导致类期望的属性与对象实际属性不匹配。
5. 存储属性
属性是关于对象的信息,也就是属于特定对象的一块数据。我们可以使用不同的引用类型来存储属性:
-
标量引用
:如果对象只包含一个属性,可以使用标量引用。
my $attribute = "green";
my $object = \$attribute;
bless $object, "Simple";
$var = ${$object};
${$object} = "red";
这种方式简单但不够灵活。
-
数组引用
:使用数组引用稍微灵活一些,可以通过数组操作来访问、添加和删除属性。
-
哈希引用
:为了获得最大的灵活性,通常使用哈希来为属性命名。
my $object = {
lastname => "Galilei",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombadier",
};
bless $object, "Person1";
这样可以方便地访问各个属性。
6. 构造函数的实现
现在我们为
Person
类创建构造函数。首先创建
Person2.pm
文件:
package Person2;
# Person2.pm
# Class for storing data about a person
use strict;
sub new {
my $self = {};
bless $self, "Person2";
return $self;
}
1;
在另一个程序中可以使用这个类创建对象:
#!/usr/bin/perl
# person2.pl
use warnings;
use strict;
use Person2;
my $person = Person2->new();
这个构造函数的工作流程如下:
1. 创建一个哈希引用:
my $self = {};
2. 将引用关联到
Person2
类:
bless $self, "Person2";
3. 返回对象:
return $self;
7. 考虑继承
为了让类更具通用性,我们需要考虑继承的情况。如果有人想从这个类继承,并且没有提供自己的构造函数,他们将使用我们的构造函数。目前的构造函数会将对象关联到
Person2
类,而不是他们自己的类。
我们需要改进构造函数,使用调用类的名称来关联对象。改进后的构造函数如下:
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
这里
shift
函数获取传递给构造函数的第一个参数,即调用类的名称。
8. 提供属性
目前创建的
Person2
对象是完全匿名的,没有任何属性。我们希望在创建对象时能够让用户指定一些属性。创建
Person3.pm
文件:
package Person3;
# Person3.pm
# Class for storing data about a person
sub new {
my $class = shift;
my $self = {@_};
bless $self, $class;
return $self;
}
用户可以这样调用构造函数:
my $object = Person3->new(
lastname => "Galii",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombardier"
);
这样就可以方便地在创建对象时指定属性。
通过以上步骤,我们从基础的对象创建,到使用现有类,再到创建自定义类,逐步深入了解了Perl的面向对象编程。希望这些内容能帮助你更好地掌握Perl面向对象编程的技巧。
Perl面向对象编程入门与实践
9. 方法的实现
在定义了类和构造函数后,我们还需要为类添加方法。方法是类中定义的子程序,用于操作对象的属性。以下是为
Person3
类添加一些简单方法的示例,将其保存为
Person3.pm
:
package Person3;
# Person3.pm
# Class for storing data about a person
use strict;
sub new {
my $class = shift;
my $self = {@_};
bless $self, $class;
return $self;
}
sub get_full_name {
my $self = shift;
return $self->{firstname} . " " . $self->{lastname};
}
sub set_address {
my ($self, $new_address) = @_;
$self->{address} = $new_address;
}
1;
在上述代码中,我们添加了两个方法:
-
get_full_name()
:用于获取对象的全名,通过拼接
firstname
和
lastname
属性。
-
set_address()
:用于设置对象的地址属性。
以下是如何使用这些方法的示例代码:
#!/usr/bin/perl
# person3_use.pl
use warnings;
use strict;
use Person3;
my $person = Person3->new(
lastname => "Galii",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombardier"
);
print "Full name: " . $person->get_full_name() . "\n";
$person->set_address("New Address");
print "New address: " . $person->{address} . "\n";
运行上述代码,你将看到输出结果显示对象的全名和更新后的地址。
10. 析构函数
当对象不再使用时,Perl会自动销毁它。在销毁之前,Perl会尝试调用名为
DESTROY()
的方法。以下是为
Person3
类添加析构函数的示例:
package Person3;
# Person3.pm
# Class for storing data about a person
use strict;
sub new {
my $class = shift;
my $self = {@_};
bless $self, $class;
return $self;
}
sub get_full_name {
my $self = shift;
return $self->{firstname} . " " . $self->{lastname};
}
sub set_address {
my ($self, $new_address) = @_;
$self->{address} = $new_address;
}
sub DESTROY {
my $self = shift;
print "Destroying object: " . $self->get_full_name() . "\n";
}
1;
在上述代码中,
DESTROY()
方法会在对象被销毁时打印一条消息,显示正在销毁的对象的全名。
11. 继承的实现
继承是面向对象编程中的一个重要概念,它允许我们创建一个新类,该类继承另一个类的属性和方法。以下是一个简单的继承示例,创建一个
Student
类,继承自
Person3
类:
package Student;
# Student.pm
# Class for storing data about a student
use strict;
use base 'Person3';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{student_id} = shift;
return $self;
}
sub get_student_id {
my $self = shift;
return $self->{student_id};
}
1;
在上述代码中:
-
use base 'Person3';
:表示
Student
类继承自
Person3
类。
-
$class->SUPER::new(@_);
:调用父类的构造函数来初始化父类的属性。
-
$self->{student_id} = shift;
:为
Student
类添加一个新的属性
student_id
。
以下是如何使用
Student
类的示例代码:
#!/usr/bin/perl
# student_use.pl
use warnings;
use strict;
use Student;
my $student = Student->new(
lastname => "Galii",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombardier",
12345
);
print "Full name: " . $student->get_full_name() . "\n";
print "Student ID: " . $student->get_student_id() . "\n";
运行上述代码,你将看到输出结果显示学生的全名和学生ID。
12. 多态的体现
多态是指不同的对象可以对相同的消息做出不同的响应。在Perl中,多态可以通过方法重写来实现。以下是一个简单的多态示例,重写
Student
类的
get_full_name()
方法:
package Student;
# Student.pm
# Class for storing data about a student
use strict;
use base 'Person3';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{student_id} = shift;
return $self;
}
sub get_full_name {
my $self = shift;
return $self->{firstname} . " " . $self->{lastname} . " (Student)";
}
sub get_student_id {
my $self = shift;
return $self->{student_id};
}
1;
以下是使用多态的示例代码:
#!/usr/bin/perl
# polymorphism_use.pl
use warnings;
use strict;
use Person3;
use Student;
my $person = Person3->new(
lastname => "Galii",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombardier"
);
my $student = Student->new(
lastname => "Galii",
firstname => "Galileo",
address => "9.81 Pisa Apts.",
occupation => "bombardier",
12345
);
print "Person full name: " . $person->get_full_name() . "\n";
print "Student full name: " . $student->get_full_name() . "\n";
运行上述代码,你将看到
Person
对象和
Student
对象对
get_full_name()
方法的不同响应。
13. 总结
通过以上内容,我们全面了解了Perl的面向对象编程。从创建对象、使用现有类,到创建自定义类、实现构造函数、方法、析构函数,再到继承和多态的应用,我们逐步深入探索了Perl面向对象编程的各个方面。
以下是一个简单的流程图,展示了创建一个完整的面向对象类的步骤:
graph TD;
A[定义包名] --> B[实现构造函数];
B --> C[添加属性];
C --> D[实现方法];
D --> E[考虑析构函数];
E --> F[处理继承];
F --> G[实现多态];
在实际应用中,我们可以根据具体需求灵活运用这些知识,创建出高效、可维护的面向对象程序。希望这些内容能帮助你在Perl编程中更好地运用面向对象的思想。
超级会员免费看
2167

被折叠的 条评论
为什么被折叠?



