在Perl中,哈希和blessed引用之间的根本区别是什么?

4

我刚接触Perl,希望了解更多关于面向对象的部分。 假设我有一个只有属性的“类”,创建一个包并将哈希表转换为引用后进行操作与直接操作哈希表相比是否有优势?

为了简单起见,我们考虑以下示例:

package Person;

sub new {
    my $class = shift;
    my $args = {Name => '', Age => 0, @_};
    
    my $self = { Name => $args->{Name},
                 Age => $args->{Age}, };
    
    bless $self, $class;
}


package main;

my $person1 = Person->new(Name => 'David', Age => 20);
my $person2 = {Name => 'David', Age => 20, Pet => 'Dog'};

print $person1->{Name} . "\n";
print $person2->{Name} . "\n";

我想知道$person1$person2的区别是什么,在面向对象方面之外,除了一个被赋予 bless 的哈希和一个哈希引用的事实?

在这种情况下,使用对象有什么好处,而不是使用哈希?

回答后:

感谢大家的帮助 :)

Håkon Hægland的评论对我来说最接近答案, 我只是想知道,如果我只需要保存简单标量,没有特殊检查,也没有其他功能,那么使用类是否比简单哈希更有优势 (我明白如果我需要额外的功能和继承,类将是正确的工具)


2
如果您不打算使用方法调用,那么差别并不大。您可以通过调用“ref $person1”来检查$person1的类名。您还可以安排Person从基类继承属性。 - Håkon Hægland
如果你正在学习Perl中的面向对象编程,那么你应该花时间学习Moo或Moose。在现代Perl中,原始的面向对象功能并不常用。 - AKHolland
@ceving,恐怕不是这样的,我知道bless的作用,但我想知道它背后是否还有其他东西。 - yshuki
1
@ceving 很抱歉没有表达清楚,我的问题是:在底层,Perl 是否会对对象与哈希表进行不同的处理,有任何优化吗?除了面向对象编程的好处和 bless 带来的作用外,是否还有其他我不知道的针对简单数据对象的处理方法? - yshuki
@HåkonHægland 如果您能发布一个答案,我可以将其标记为问题的答案。 - yshuki
显示剩余3条评论
2个回答

6

$person1 是一个已经被 bless 的引用。为了创建对象,Perl 将引用数据与包名相关联。当您将该引用视为对象时,Perl 使用该包名来查找方法。

$person2 只是一个引用。

由于两者仍然只是引用,因此您仍然可以将它们视为引用。这就是为什么这仍然有效:

print $person1->{Name} . "\n";
print $person2->{Name} . "\n";

你使用哪种方法取决于你运用的场景。工具没有超出其背景的实用性。
你可以阅读很多关于面向对象思想的书籍。使用普通哈希表,你请求一个键,就会得到一个值。在面向对象接口中,你使用方法要求对象为你做事情。这可能会返回一个值,或者做其他任何你喜欢的事情。你不需要知道对象是如何完成工作的。
面向对象接口可以保持稳定,而底层数据或结构可以改变。使用普通哈希表时,你的程序必须意识到哈希结构的变化。
在像你提供的简单示例中,你看不到太多好处,因为你将接口直接映射到结构上。如果你看更复杂的示例(例如复杂的Perl模块),你会发现你可以访问和操作的东西不直接映射到它们所使用的数据结构上。
例如,假设你的任务的某个部分基于某人的年龄来区分任务(例如存储人们数据的各种法律或法规)。也许你有一个方法 is_underage:
if( $person2->is_underage ) { ... }

在IT技术中,对于这个年龄不一定要以强硬的答案来进行存储,尤其是因为这个年龄因管辖区和活动而异。但是,模块会以某种方式找出答案。这就是接口的目的:隐藏复杂性。

举个例子,请参阅如何将脚本转化为模块。在一个扩展示例中,我从简单的事情开始,但随着问题变得更加复杂,我开始移动东西,并最终得到一个模块。但是,其中一部分故事是认识到对象可以使事情变得更简单。

这是通过经验获得的知识。两种方式都尝试一下。寻找在任何一种方式中努力过度的迹象。例如,有些人已经推荐了Moo,但请考虑是否需要引入模块来执行简单的操作。同样,如果您正在制作一个大型系统,则可能需要框架。

有很多人主张完全遵守OO,但同样也有一个(错误 :) 的阵营主张始终使用裸数据结构。函数式和面向对象的风格不必是敌人,特别是在Perl中。


除了更有意义外,这使得引用变得更小,并且使复制引用(例如 return $objpush @objs,$obj;等)更快。 - ikegami
你在技术上是正确的(最好的形式),但我只想解释这么多事情。 - brian d foy
感谢您提供的信息丰富的答案。我发布的示例已经尽可能接近我所需的内容。假设我只需要保存简单数据(仅标量),没有函数,没有处理,没有特殊情况,我只是想知道在 Perl 的底层中,是否有任何 OO 相对于简单哈希的优势。 - yshuki
我并没有建议你进行任何额外的解释,只是要避免不必要的误导。说“所引用的哈希已被祝福”和说“引用已被祝福”一样容易。 - ikegami

1
如果你不打算使用方法调用,那么区别就不是很大。你可以使用$person1来获取它所属的类名,方法是调用ref $person1。你还可以安排Person从一个基类中继承属性。
另一件你可以做的事情是为Person提供对其数据属性的访问验证。因此,你将会实现一个name()方法,它返回$person1->{Name}并可能执行其他有用的操作,例如记录日志或检查名称是否已定义等。
例如:
#! /usr/bin/env perl

package LivingBeing;
use strict;
use warnings;
sub new {
    die "LivingBeing: Wrong number of arguments to the constructor!" if @_ != 2;
    my ( $class, $type ) = @_;

    return bless {type => $type}, $class;
}

package Person;
use strict;
use warnings;
use parent -norequire => qw(LivingBeing);
sub new {
    my ($class, %args) = @_;
    my $self = $class->SUPER::new('human');
    $self->{age} = 0;  # Default value
    $self->{$_} = $args{$_} for keys %args;  # Maybe override default values..
    return $self;
}

sub name {
    my $self = shift;
    my $name = $self->{name};
    warn "Undefined name attribute" if !defined $name;
    return $name;
}

package main;
use strict;
use warnings;
use feature qw(say);

my $person1 = Person->new(pet => 'cat', age => 20);
my $person2 = {name => 'David', age => 20, pet => 'dog'};

say "person1 is a : ", ref $person1;
say "person2 is a : ", ref $person2;

say "The name of person1 is: ", $person1->name;
say "The age of person1 is: ", $person1->{age};

输出:

person1 is a : Person
person2 is a : HASH
Undefined name attribute at ./p2.pl line 28.
Use of uninitialized value in say at ./p2.pl line 43.
The name of person1 is: 
The age of person1 is: 20

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接