我正在编写一个perl类,为了这个问题,我将把它称为“学生”。学生类会有很多方法,为了整洁和效率,我不想把它们都放在一个源文件中。
假设Student.pm是package Student,包含sub new (构造函数),而say /Enrol.pm是package Student::Enroll,包含sub enroll (一个方法)。我写的sub enroll是这样的:
sub enroll
{
my ($student) = @_;
# do something;
return;
}
*Student::enroll = \&enroll;在我的主程序中,我可以编写my $student = new Student()和$student->enroll()。这是可行的,但它不透明。它有什么我忽略的缺点吗?
我还没有尝试从package::Enroll导出enroll到package中。这不是我想要的,因为那样的话,我需要在学生包中写use Student::Enroll,我不想这样做。有些程序需要学生包,但不需要学生::登记。
是否有一种更好、更干净的方法来编写sub enroll /Enrol.pm,从而使它成为一个学生方法?
发布于 2015-03-25 19:40:38
可以在sub声明中使用包限定符。
package Student::Enroll;
...
sub foo { ... } # Student::Enroll::foo
sub Student::enroll { ... } # Student::enroll, not Student::Enroll::enroll
...当然,如果Student::enroll在Student/Enroll.pm中的唯一定义,以及某些程序需要Student::enroll函数,那么该程序将不得不加载Student/Enroll.pm和所有的Student::Enroll包。
发布于 2015-03-25 23:49:37
它可以很容易地用自编方法来完成。
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::$2/;
my $package = $1;
my $method = $2;
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::$1/;
&$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');发布于 2015-03-26 15:56:45
首先,我建议以“现代Perl”的方式开始编程,远离老式的Perl5祝福对象散列.
use Moose;这是一个适合Perl的OO系统,借用自Perl6。
当你的课变得“大”的时候,是时候重新考虑什么是真正进入课堂的,什么是不应该出现的。有些属性和方法实际上可能是非常通用的,并且可以从超类继承。但是Moose (和Moo)也有“角色”,这种行为可以添加到另一个类之上。
让我展示一下我的“学生”班是什么样子的:
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行:我们使用另一个类Person,并将其用作基类。现在,在本例中,我确实创建了一个College::Student类,一个比通用Person类更具体的类(也不是在College命名空间中)。
第07行:特定于这个College::Student类,这里只使用一个student_registration_number,它是一个整数类型的只读属性,是必需的。
第12行:使用角色College::Enrolment,我们希望学生有更多的行为--这样它就可以注册一门课程,并且需要什么方法和属性才能做到这一点,这在这里并不重要,我们在这里想要做的就是它能做到这一点。
现在来看一下“学院::招生”课程的内部情况:
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行:这是一个驼鹿::角色,可以应用于其他对象.你不能实例化这些!
线05:啊..。显然,有一个enroll实例方法,
第16行:以及一个un_enroll实例方法。
为了完整起见,基类Person看起来非常基本,不适合学生。
package Person;
use Moose;
has name => (
is => 'ro',
isa => 'Str',
required => 1,
);
has date_of_birth => (
is => 'ro',
isa => 'DateTime',
required => 1,
);
1;还可以对类似于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创建一个类,它可能没有注册号,但是需要能够注册--使用相同的角色。
在这里踢驼鹿的力量..。
与其创建庞大的类,不如尝试拆分尽可能多的角色,这些角色是方法及其属性的逻辑组合。这使得它更易于维护和测试。很快,您将看到角色是构建类的一种更符合逻辑的方法,而不是尝试进行继承--或者更糟糕的是,多继承。
哦..。缺少的东西:
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__别忘了读穆斯的故事
2014/07-object-oriented-perl.html
http://www.theperlreview.com/articles/moose.html
https://stackoverflow.com/questions/29264837
复制相似问题