一些关于 Perl 的小提示:
- 遇到此类问题时使用Data::Dumper。
Data::Dumper 将帮助您了解结构。
- 如果您打算完全面向对象,那就全力以赴。你有构造函数和方法。方法应该返回值,并且您对这些值进行操作。每个方法和构造函数都应尽可能独立,并且对您的整体对象结构知之甚少。
- 在
main 包中运行。你应该翻转你的程序。最后两个语句应该在顶部,并在下面定义您的包。
如果你使用Data::Dumper 并在调用它时转储$self 的数据,你会发现$self 是一个包含三个键的散列,并且options 键指向一个匿名数组。
为了做你想做的事,你必须取消引用那个匿名数组:
sub print_options {
my($self) = @_;
for ( @{$self->{options} } ) {
print $_ . "\n";
}
}
但是,让我们清理您的程序以使其成为一个完整的类。首先,方法从不打印——它们返回值。因此,您不应该有 print_options 方法。相反,它应该返回数组(或对数组的引用)。
此外,您的构造函数不必知道您的Options 的结构。如果你改变它呢?让我们看一下更改后的程序:
#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
my $setup = Foo->new;
for my $option ( $setup->Option ) {
say $option;
}
package Foo;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->Option("PHP + Apache");
$self->Option("PHP + Lighthttpd");
$self->Option("PHP + Nginx");
$self->Option("RubyGems + Rails");
$self->Option("Node JS + NPM");
return $self;
}
sub Distro {
my $self = shift;
my $distro = shift;
if ( defined $distro ) {
$self->{DISTRO} = $distro;
}
return $self->{DISTRO};
}
sub Package_Manager {
my $self = shift;
my $package = shift;
if ( defined $package ) {
$self->{PACKAGE} = $package;
}
return $self->{PACKAGE};
}
sub Option {
my $self = shift;
my $option = shift;;
if ( not exists $self->{OPTION} ) {
$self->{OPTION} = [];
}
if ( defined $option ) {
push @{ $self->{OPTION} }, $option;
}
my @array = @{ $self->{OPTION} };
return wantarray ? @array : \@array;
}
首先,与 PHP 不同,Perl 在执行之前编译代码。因此,您可以在程序底部定义包及其子例程。以上几行代码在默认的main包中运行。如果我确实在 Foo 包中使用了包变量,我不会不小心在我的程序中使用它。
请注意,我的方法或构造函数都不知道Foo 对象的结构。当我在new 构造函数中设置选项时,我调用Option 方法进行设置。这样,如果我更改存储选项的方式,我只需要更改我的 Option 方法,而不必担心我的包中的其他地方必须更改它。它还简化了我的程序并使其更易于支持。
通过使我的Option 方法设置或获取我的选项,我简化了代码。我可以看到我将选项存储在一个匿名数组中,很明显我需要在返回对象时 dereference 它。我喜欢使用wantarray 并让用户选择是想要数组还是引用数组。
请注意,我不会简单地返回 $self->{OPTION}。它是对数组的引用,应该可以工作,但是如果我返回它,我将返回该对象的内存位置!因此,如果用户更改了该引用,他们将更改我的对象而无需通过我的方法。因此,我创建了另一个数组,并返回对它的引用。随心所欲地使用那个数组引用,你并没有改变我的对象。
此代码不完整。我可以设置选项,但不能取消设置选项。有一种方法来 push 和 pop 选项可能会很好。相反,此时您所能做的就是返回整个列表,而不是修改它。