26、Perl面向对象编程入门与实践

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) | 将文件上传到远程服务器 |

  1. 关闭连接:
$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编程中更好地运用面向对象的思想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值