在perl中,如何在多个包中编写方法?
In perl, how can I write methods in multiple packages?
我正在写一个 perl class,出于这个问题的目的,我将其称为 Student。 Studentclass会有很多方法,为了整洁和效率,我不想把它们都放在一个源文件中。
假设 Student.pm 是 package Student
并且包含 sub new
构造函数,而 Student/Enroll.pm 是 package Student::Enroll
并且包含 sub enroll
,一个方法。我这样写 sub enroll
:
sub enroll
{
my ($student) = @_;
# do something;
return;
}
*Student::enroll = \&enroll;
在我的主程序中,我可以写my $student = new Student()
和$student->enroll()
。这有效,但它是不透明的。它有任何我忽略的缺点吗?
我还没有尝试将包 Student::Enroll 中的 enroll
导出到包 Student 中。这不是我想要的,因为那时我需要在 Student 包中编写 use Student::Enroll
,而我不想那样做。会有需要 Student 包但不需要的程序 Student::Enroll.
在Student/Enroll.pm 中是否有更好、更简洁的方法来编写sub enroll
,使其成为Student 方法?
您可以在 sub
声明中使用包限定符
package Student::Enroll;
...
sub foo { ... } # Student::Enroll::foo
sub Student::enroll { ... } # Student::enroll, not Student::Enroll::enroll
...
当然如果在Student/Enroll.pm
中只定义了Student::enroll
,而某些程序需要Student::enroll
函数,那么该程序将不得不加载Student/Enroll.pm
和所有的Student::Enroll
包。
借助 AUTOLOAD 方法可以轻松完成。
package Student::Enroll;
use strict;
use warnings;
use v5.14;
sub enroll { say "Good luck $_[1]"; }
1;
和
新版本:
package Student;
use strict;
use warnings;
use v5.14;
use Carp qw/croak/;
{
my $_additional_methods = {
enroll => undef
};
sub _can_access {
exists $_additional_methods->{$_[0]}
}
}
sub new { bless {}, shift }
# Subroutine AUTOLOAD will be called always
# when someone calls a method (a subroutine) which
# isn't present in this module.
# In our case "enroll" will be called from AUTOLOAD
# only one and next time of calling it will be called
# directly as a normal method of the class since
# we create a typeglob ref for it.
sub AUTOLOAD {
no strict 'refs';
our $AUTOLOAD; # in this global variables are kept
# the full name of a called method
my $package_method = $AUTOLOAD;
# making up the full method name
$AUTOLOAD =~ s/(.*)::(\w+)$/Student::Enroll::/;
my $package = ;
my $method = ;
if ( _can_access($method) ) {
eval 'use Student::Enroll;'; # loading of the necessary class
&$AUTOLOAD(@_); # calling the method from Student::Enroll class
no warnings 'redefine';
*{$package_method} = \&AUTOLOAD;
} else {
croak "Can't locate object method '$method' via package '$package'";
}
}
package main;
use strict;
use warnings;
my $student = Student->new;
$student->enroll('David');
$student->enroll('David');
$student->enoll('David');
旧版本:
package Student;
use strict;
use warnings;
use v5.14;
sub new { bless {}, shift }
# Subroutine AUTOLOAD will be called always
# when someone calls a method (a subroutine) which
# isn't present in this module (enroll in our case)
sub AUTOLOAD {
no strict 'refs';
our $AUTOLOAD; # in this global variables are kept
# the full name of a called method
eval 'use Student::Enroll;'; # loading of the necessary class
# making up the full method name
$AUTOLOAD =~ s/.*::(\w+)$/Student::Enroll::/;
&$AUTOLOAD; # calling the method from Student::Enroll class
# Note: array @_ of args is passed automatically
}
package main;
use strict;
use warnings;
my $student = Student->new;
$student->enroll('David');
首先,我建议以 'Modern Perl' 的方式开始编程,并远离老式的 Perl5 blessed hashes for object...
use Moose;
这是一个适合 Perl 的 OO 系统,借鉴自 Perl6。
当您的 class 变成 'big' 时,是时候重新考虑 class 中实际包含的内容以及不应该包含的内容了。某些属性和方法实际上可能非常通用,可以从超级 class 继承。但是 Moose(和 Moo)也有 'Roles',可以添加到另一个 class.
之上的行为
让我展示一下我的 'Student' class 的样子:
package College::Student;
use Moose;
extends 'Person';
has 'student_registration_number' => (
is => 'ro',
isa => 'Int',
required => 1,
);
with 'College::Enrolment';
1;
发生了什么:
第 01 行:声明包的名称,College::Student
,大学相关内容的单独命名空间。
第 03 行:use Moose;
自然!
第 05 行:我们正在使用另一个 class、Person
并将其用作基础 class。现在,在这种情况下,我确实创建了一个 College::Student
class,一个更具体的 class,然后是一个通用的 Person
class(不在 College
命名空间)。
第07行:具体到这个College::Student
class,只是这里我们用了一个student_registration_number
,它是一个整数类型的只读属性,是必须的。
第 12 行:对于 College::Enrolment
这个角色,我们希望学生有额外的行为 - 这样它可能可以注册一门课程,以及为了做到这一点需要什么方法和属性,它们是在这里并不重要,我们在这里要关心的是它能做到这一点。
现在简单看一下'College::Enrolment' class:
package College::Enrolment;
use Moose::Role;
sub enroll {
my $self = shift; # a person
my $args = {@_};
print
$self->name,
" is being enrolled into: ",
$args->{'course'}->course_title,
"\n",
;
};
sub un_enroll {
};
1;
第 01 行:一个不错的命名空间,又与大学有关。
第 03 行:这是一个可以应用于其他对象的 Moose::Role...您不能实例化这些对象!
第05行:啊...显然,有一个enroll
实例方法,
第 16 行:还有一个 un_enroll 实例方法。
为了完整起见,基础 class Person
看起来很基础,没有针对学生的具体内容。
package Person;
use Moose;
has name => (
is => 'ro',
isa => 'Str',
required => 1,
);
has date_of_birth => (
is => 'ro',
isa => 'DateTime',
required => 1,
);
1;
也可以将此 class 扩展为 College::Professor
,例如:
package College::Professor;
use Moose;
extends 'Person';
has 'employee_number' => (
is => 'ro',
isa => 'Int',
required => 1,
);
with 'College::Enrolment'
1;
现在,College::Professor
是 Person
,但是它没有 College::Enrolment
角色。
如果您愿意,为 College::GuestStudent
设置一个 class 也很容易,它可能没有注册号,但确实需要能够注册 --- 同样角色。
这里开始发挥 Moose Roles 的作用...
与其创建巨大的 classes,不如尝试拆分尽可能多的角色,这些角色是方法及其属性的逻辑组合。这使它更易于维护和测试。很快您就会发现角色是构建 classes 的一种更合乎逻辑的方式,而不是尝试继承 - 或者更糟的是多重继承。
哦..少了点东西:
use strict;
use warnings;
use College::Student;
use College::Course;
use DateTime::Format::ISO8601;
my $study = College::Course->new(
course_title => "French for beginners"
);
my $pupil = College::Student->new(
name => "John Doe",
date_of_birth => DateTime::Format::ISO8601->parse_datetime("2001-07-10"),
student_registration_number
=> '123456',
);
$pupil->enroll( course => $study );
__END__
别忘了在
阅读有关 Moose 的文章
http://modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html
我正在写一个 perl class,出于这个问题的目的,我将其称为 Student。 Studentclass会有很多方法,为了整洁和效率,我不想把它们都放在一个源文件中。
假设 Student.pm 是 package Student
并且包含 sub new
构造函数,而 Student/Enroll.pm 是 package Student::Enroll
并且包含 sub enroll
,一个方法。我这样写 sub enroll
:
sub enroll
{
my ($student) = @_;
# do something;
return;
}
*Student::enroll = \&enroll;
在我的主程序中,我可以写my $student = new Student()
和$student->enroll()
。这有效,但它是不透明的。它有任何我忽略的缺点吗?
我还没有尝试将包 Student::Enroll 中的 enroll
导出到包 Student 中。这不是我想要的,因为那时我需要在 Student 包中编写 use Student::Enroll
,而我不想那样做。会有需要 Student 包但不需要的程序 Student::Enroll.
在Student/Enroll.pm 中是否有更好、更简洁的方法来编写sub enroll
,使其成为Student 方法?
您可以在 sub
声明中使用包限定符
package Student::Enroll;
...
sub foo { ... } # Student::Enroll::foo
sub Student::enroll { ... } # Student::enroll, not Student::Enroll::enroll
...
当然如果在Student/Enroll.pm
中只定义了Student::enroll
,而某些程序需要Student::enroll
函数,那么该程序将不得不加载Student/Enroll.pm
和所有的Student::Enroll
包。
借助 AUTOLOAD 方法可以轻松完成。
package Student::Enroll;
use strict;
use warnings;
use v5.14;
sub enroll { say "Good luck $_[1]"; }
1;
和
新版本:
package Student;
use strict;
use warnings;
use v5.14;
use Carp qw/croak/;
{
my $_additional_methods = {
enroll => undef
};
sub _can_access {
exists $_additional_methods->{$_[0]}
}
}
sub new { bless {}, shift }
# Subroutine AUTOLOAD will be called always
# when someone calls a method (a subroutine) which
# isn't present in this module.
# In our case "enroll" will be called from AUTOLOAD
# only one and next time of calling it will be called
# directly as a normal method of the class since
# we create a typeglob ref for it.
sub AUTOLOAD {
no strict 'refs';
our $AUTOLOAD; # in this global variables are kept
# the full name of a called method
my $package_method = $AUTOLOAD;
# making up the full method name
$AUTOLOAD =~ s/(.*)::(\w+)$/Student::Enroll::/;
my $package = ;
my $method = ;
if ( _can_access($method) ) {
eval 'use Student::Enroll;'; # loading of the necessary class
&$AUTOLOAD(@_); # calling the method from Student::Enroll class
no warnings 'redefine';
*{$package_method} = \&AUTOLOAD;
} else {
croak "Can't locate object method '$method' via package '$package'";
}
}
package main;
use strict;
use warnings;
my $student = Student->new;
$student->enroll('David');
$student->enroll('David');
$student->enoll('David');
旧版本:
package Student;
use strict;
use warnings;
use v5.14;
sub new { bless {}, shift }
# Subroutine AUTOLOAD will be called always
# when someone calls a method (a subroutine) which
# isn't present in this module (enroll in our case)
sub AUTOLOAD {
no strict 'refs';
our $AUTOLOAD; # in this global variables are kept
# the full name of a called method
eval 'use Student::Enroll;'; # loading of the necessary class
# making up the full method name
$AUTOLOAD =~ s/.*::(\w+)$/Student::Enroll::/;
&$AUTOLOAD; # calling the method from Student::Enroll class
# Note: array @_ of args is passed automatically
}
package main;
use strict;
use warnings;
my $student = Student->new;
$student->enroll('David');
首先,我建议以 'Modern Perl' 的方式开始编程,并远离老式的 Perl5 blessed hashes for object...
use Moose;
这是一个适合 Perl 的 OO 系统,借鉴自 Perl6。
当您的 class 变成 'big' 时,是时候重新考虑 class 中实际包含的内容以及不应该包含的内容了。某些属性和方法实际上可能非常通用,可以从超级 class 继承。但是 Moose(和 Moo)也有 'Roles',可以添加到另一个 class.
之上的行为让我展示一下我的 'Student' class 的样子:
package College::Student;
use Moose;
extends 'Person';
has 'student_registration_number' => (
is => 'ro',
isa => 'Int',
required => 1,
);
with 'College::Enrolment';
1;
发生了什么:
第 01 行:声明包的名称,College::Student
,大学相关内容的单独命名空间。
第 03 行:use Moose;
自然!
第 05 行:我们正在使用另一个 class、Person
并将其用作基础 class。现在,在这种情况下,我确实创建了一个 College::Student
class,一个更具体的 class,然后是一个通用的 Person
class(不在 College
命名空间)。
第07行:具体到这个College::Student
class,只是这里我们用了一个student_registration_number
,它是一个整数类型的只读属性,是必须的。
第 12 行:对于 College::Enrolment
这个角色,我们希望学生有额外的行为 - 这样它可能可以注册一门课程,以及为了做到这一点需要什么方法和属性,它们是在这里并不重要,我们在这里要关心的是它能做到这一点。
现在简单看一下'College::Enrolment' class:
package College::Enrolment;
use Moose::Role;
sub enroll {
my $self = shift; # a person
my $args = {@_};
print
$self->name,
" is being enrolled into: ",
$args->{'course'}->course_title,
"\n",
;
};
sub un_enroll {
};
1;
第 01 行:一个不错的命名空间,又与大学有关。
第 03 行:这是一个可以应用于其他对象的 Moose::Role...您不能实例化这些对象!
第05行:啊...显然,有一个enroll
实例方法,
第 16 行:还有一个 un_enroll 实例方法。
为了完整起见,基础 class Person
看起来很基础,没有针对学生的具体内容。
package Person;
use Moose;
has name => (
is => 'ro',
isa => 'Str',
required => 1,
);
has date_of_birth => (
is => 'ro',
isa => 'DateTime',
required => 1,
);
1;
也可以将此 class 扩展为 College::Professor
,例如:
package College::Professor;
use Moose;
extends 'Person';
has 'employee_number' => (
is => 'ro',
isa => 'Int',
required => 1,
);
with 'College::Enrolment'
1;
现在,College::Professor
是 Person
,但是它没有 College::Enrolment
角色。
如果您愿意,为 College::GuestStudent
设置一个 class 也很容易,它可能没有注册号,但确实需要能够注册 --- 同样角色。
这里开始发挥 Moose Roles 的作用...
与其创建巨大的 classes,不如尝试拆分尽可能多的角色,这些角色是方法及其属性的逻辑组合。这使它更易于维护和测试。很快您就会发现角色是构建 classes 的一种更合乎逻辑的方式,而不是尝试继承 - 或者更糟的是多重继承。
哦..少了点东西:
use strict;
use warnings;
use College::Student;
use College::Course;
use DateTime::Format::ISO8601;
my $study = College::Course->new(
course_title => "French for beginners"
);
my $pupil = College::Student->new(
name => "John Doe",
date_of_birth => DateTime::Format::ISO8601->parse_datetime("2001-07-10"),
student_registration_number
=> '123456',
);
$pupil->enroll( course => $study );
__END__
别忘了在
阅读有关 Moose 的文章http://modernperlbooks.com/books/modern_perl_2014/07-object-oriented-perl.html