曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 5345|回复: 0
打印 上一主题 下一主题

[OOP] 闭包与对象

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2011-11-2 01:24:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
操作对象,普通的方法是返回对象的一个引用,而我们一拿到这个引用就可以直接的修改对象中的数据,即使我们在模块中提供了操作对象的方法,但只要用户不愿意,那么也就无法阻止它们直接访问对象中的数据。如下图所示:



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
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-5 17:09 , Processed in 0.068223 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表