|
操作对象,普通的方法是返回对象的一个引用,而我们一拿到这个引用就可以直接的修改对象中的数据,即使我们在模块中提供了操作对象的方法,但只要用户不愿意,那么也就无法阻止它们直接访问对象中的数据。如下图所示:
Perl 并没有为类数据提供专门的私有区域(private section),这可能会遭致缺乏面向对象中私有机制理念的批评。针对这个问题,Perl 提供了解决途径之一便是 闭包。
闭包概念,往简单了说,就是一个匿名子例程。使用闭包机制,可以实现对数据的封装,也就是说用户不能直接操作对象中的数据,而只能通过类中给定的方法,由方法操作闭包,再由闭包来操作数据,这样一来闭包就如同用户和数据之间的一个中转者,如下图所示:
下面用代码来解释上面的描述。
1. 先创建一个 Student.pm 的模块,代码如下:
[code=perl]package Student;
sub new {
my $class = shift;
my $data = {};
#全局变量,用来统计脚本中创建 Student 对象的数目
our $students;
#闭包
my $ref = sub {
my ($access_type, $key, $value) = @_;
if ($access_type eq "set") {
$data->{$key} = $value;
}
elsif ($access_type eq "get") {
return $data->{$key};
}
elsif ($access_type eq "keys") {
return (keys %{$data});
}
elsif ($access_type eq "destroy") {
$students--;
return $students;
}
else {
die "Access type should be set or get";
}
};
print "New student created, we have ", ++$students, " students.\n";
#将闭包归入到Student类中,这样用户就可以通过类的方法来操作这个闭包,而闭包中含有的要操作的对象数据
bless($ref, $class);
#可以不用显式 return $ref,默认返回
}
sub set {
#$self 是对闭包的引用
my ($self, $key, $value) = @_;
$self->("set", $key, $value);
}
sub get {
my ($self, $key) = @_;
return $self->("get", $key);
}
sub display {
my $self = shift;
my @keys = $self->("keys");
@keys = reverse(@keys);
foreach my $key (@keys) {
my $value = $self->("get", $key);
printf "%-25s%-5s:%-20s\n", $self, $key, $value;
}
print "\n";
}
sub DESTROY {
my $self = shift;
print "Object going out of scope:\n";
print "Students remain: ", $self->("destroy"), "\n";
}
1;[/mw_shl_code]
使用上述模块的脚本:
[code=perl]#!/usr/bin/perl
use Student;
$ptr1 = Student->new();
$ptr2 = Student->new();
$ptr3 = Student->new();
#设置对象属性(姓名和专业)
$ptr1->set("Name", "Jody Rogers");
$ptr1->set("Major", "Drama");
$ptr2->set("Name", "Christian Dobbins");
$ptr2->set("Major", "Law");
$ptr3->set("Name", "Tina Savage");
$ptr3->set("Major", "Art");
$ptr1->display();
$ptr2->display();
$ptr3->display();
print "\nThe major for ", $ptr1->get("Name"), " is ", $ptr1->get("Major"), ".\n\n";[/mw_shl_code]
运行输出:[beyes@beyes student]$ ./stu.pl
New student created, we have 1 students.
New student created, we have 2 students.
New student created, we have 3 students.
Student=CODE(0x8c93960) Name :Jody Rogers
Student=CODE(0x8c93960) Major:Drama
Student=CODE(0x8cad2f0) Name :Christian Dobbins
Student=CODE(0x8cad2f0) Major:Law
Student=CODE(0x8cb2d48) Name :Tina Savage
Student=CODE(0x8cb2d48) Major:Art
The major for Jody Rogers is Drama.
Object going out of scope:
Students remain: 2
Object going out of scope:
Students remain: 1
Object going out of scope:
Students remain: 0 在 Student.pm 模块的构造函数 new 中,它为每个新的 Student 对象设置属性值为空的匿名散列,这些散列中将会用来存放学生的姓名以及专业信息,而存放这一动作(set)是通过类的方法去操控闭包中的代码来实现的。同样,访问数据也是通过方法 get 操控闭包中的代码块来达到获取数据的目的。
在 Student.pm 中还定义了一个全局变量 $students ,它可以用来统计创建的 Student 对象的数目。
最后定义了析构函数 (destructor) 方法,它负责在销毁每个 Student 对象时显示一些内容。
那么回过来考虑如果用代码试图直接来访问对象输入会如何:
[code=perl]#!/usr/bin/perl
use Student;
$ptr1 = Student->new();
$ptr1->("Name", "Jody Gibert");[/mw_shl_code]
运行输出:[beyes@beyes student]$ ./direct_access.pl
New student created, we have 1 students.
Access type should be set or get at Student.pm line 28.
Object going out of scope:
Students remain: 0 从输出的提示可以看到,由于我们在闭包中做了 $access_type 变量的检查,而该用户没法直接操作对象数据。当然了,因为我们此时的引用是对闭包的引用,而我们又知道了 set 方法的实现方法(我们看了实现方法的代码,而不是只了解 set 方法的用法),那么我们仍然可以在程序中着么指定:
[Plain Text] 纯文本查看 复制代码 $ptr1->("set", "Name", "Jody Gibert");
这种方式其实和 set 方法的实现是一样的道理,所以它也能访问数据。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|