6

新手用户的第一篇文章。我在谷歌上搜索的每一个问题似乎都会把我带到这里,而且我总是能得到很好的答案。所以当我开始思考 Perl 中祝福的用法时,很自然这是我的第一站。

我刚刚进入 Perl 的 OOP,今天刚刚阅读了帖子,询问 bless 是做什么的。我现在明白它引用了一个标量/哈希/数组到一个对象,如果你愿意的话,“附加”它。

在我在 Perl 中看到的大多数类示例中,它们似乎没有我在其他语言中看到的属性......

{ package Person;
    my $property = "This is what I'm talking about :)";

    sub new { ... }
    ...
}

所以,我创建了一个带有属性的愚蠢类,看看会发生什么。我立即给该属性值“NIL”,然后“Not Nil!” 在构造函数中。使用方法 LIST,我能够打印该属性,并且正如我所料,它打印了“Not Nil!”

我的问题是,如果属性的工作方式与我预期的一样(在正文中声明),那么为什么要使用 bless 呢?当您可以简单地将标量/哈希/数组创建为属性,或者创建您想要的任何引用作为属性时,拥有该引用的额外好处是什么?

我希望我能很好地解释我想问的问题,用 Perl 非常绿色 :)

4

4 回答 4

11

好吧,这不是您在 Perl 中创建类的方式。

您的$property变量在包范围中定义。因此,每个类只有一个副本,而不是每个对象都有自己的副本。

可以使用基于散列的对象来实现这样一个类,方法如下:

#!/usr/bin/perl

package Person;

use strict; use warnings;

sub new {
    my $class = shift;
    my $self = {};
    bless $self => $class;

    my ($arg) = @_;
    for my $property ( qw( message ) ) {
        if ( exists $arg->{$property} ) {
            $self->$property($arg->{$property});
        }
    }
    return $self;
}

sub message {
    my $self = shift;
    return $self->{message} unless @_;
    my ($msg) = @_;
    $self->{message} = $msg;
}

package main;

my $person = Person->new({
    message => "This is what I'm talking about :)"
});

print $person->message, "\n";

现在,这很快就会变得乏味。因此,有一些模块可以帮助您处理这个问题,并帮助您以对继承安全的方式定义您的类。

Class::Accessor就是这样一个实用模块。

对于启动时间不是问题的程序,您应该考虑Moose。使用 Moose,您可以将上述内容编写为:

#!/usr/bin/perl

package Person;

use Moose;

has 'message' => (is => 'rw', isa => 'Str');

__PACKAGE__->meta->make_immutable;
no Moose;

package main;

my $person = Person->new({
    message => "This is what I'm talking about :)"
});

print $person->message, "\n";

您应该阅读perldoc perltootMoose::Manual::Unsweetened以了解标准的做事方式。

于 2010-08-11T04:00:23.323 回答
8

在这种情况下,您对 $property 所做的事情被声明为“Person”包范围内的变量。您在包的内部(或外部使用 $Person::property)更改它,任何引用它的对象都会看到更新的变量,因此它的行为很像“静态属性(Java)”,没有任何真正的“私有” “ 范围。按照惯例,Perl 中的隐藏内容(“private”或“protected”)都带有下划线前缀,但当然这不是强制的。

正如您所指出的,您实际上并没有使用“package”关键字创建一个新类;您完全可以在没有 OOP 的情况下使用“包”。这只是创建了一个单独的“命名空间”。

“祝福”变量的优势,几乎总是我所看到的哈希引用,是你可以拥有方法,就像任何其他 OOP 语言一样。只要记住祝福你在 new {} 子例程中返回的任何内容(“new”实际上不是保留字;只是一个约定)。当您在“对象”(像 hashref 之类的受祝福的数据结构)上调用方法时,该方法的第一个参数是数据结构本身。所以,如果你有一个名为 $myobject 的 hashref,它被 AwesomeClass 所祝福,并且你在 AwesomeClass 中定义了一个名为 doSomethingAwesome 的方法,它需要接受一个变量,你将不得不“转移”@_(这是子例程,或使用 $_[0]) 访问 $myobject hashref。Python做了类似的事情,并且所有语言都以某种方式将对象引用传递给方法。(许多中的“this”关键字,另见“thiscall”调用约定)

NB:在我作为程序员只有几年的时间里,我看到了很多 Perl 抨击。Perl 是一种非常棒的语言,它由一位非常聪明的语言学家(Larry Wall)创造,并且拥有狂热的追随者——也许一度比 Ruby 更狂热,但不如 David Koresh)。Perl 做的事情与许多语言非常不同,但是如果您查看本网站和其他网站上的代码高尔夫条目,您可以清楚地看到只需很少的 Perl 就可以完成很多事情(不能保证代码的易读性,尤其是对于新手!)

于 2010-08-11T03:55:56.653 回答
5

'ing 对象的价值bless在于使用特定包中的方法。

package MyClass;
sub answer { my ($self)=@_; return $self->{foo} * 42; }

package main;
my $object1 = { foo => 1, bar => "\t" };
my $object2 = bless { foo => 2, bar => "\t" }, "MyClass";

$ref1 = ref $object1;        #  'HASH'
$ref2 = ref $object2;        #  'MyClass'

$answer1 = $object1->answer;     # run time error
$answer2 = $object2->answer;     # calls MyClass::answer, returns 2 * 42 = 84
于 2010-08-11T04:44:54.560 回答
4

呃……思南的回答对我的口味来说太博学了,至少在上午 12 点过去 :)

因此,为了多样化,我将给出一个更短且更少 Perly 的版本。

据我所知,您的问题并不是关于 Perl的,并且可以很容易地以另一种形式出现:“当 C 已经有结构时,为什么还要在其中使用 C++ 和 OOP?”

换句话说,您似乎在问使用 OOP 范式的意义何在。

答案当然是它比纯过程编程更容易解​​决某些软件工程问题。强调某些- OOP 并不是解决所有问题的灵丹妙药,任何技术/方法/范式都不是。

使用 OOP(在 Perl 中以包的形式作为类和作为对象的祝福散列)可以让您享受继承、多态性和其他 OOPyish mumbo-jumbo 的好处,您可能已经从非 Perl OOP 经验中相当熟悉了。

你能做到 100% 用纯数据结构的祝福对象完成的工作吗?绝对地。100% 的代码会像使用对象实现的一样简单/简短/可读/可维护吗?很可能不会,尽管这取决于您的 OOP 代码实际上如何利用 OOP 提供的好处(顺便说一句,我遇到了所谓的 OOP 代码(Perl 和非 Perl),它并没有真正利用 OOP 范式并且可以如果去掉了 OOP chrome,则更易于阅读和理解)。

于 2010-08-11T04:19:13.753 回答