如何使用可扩展代码编写Perl对象?我在思考驱动程序或一些配置,用户可以在其中传递字符串“Classname :: Class”或其他东西。感谢。
例如,对于图表类:
my $spg = Graph::ShortestPathGraph->new;
$spg->Algorithm( "Graph::DFS" );
$spg->solve;
$spg->Algorithm( "Graph::BFS" );
$spg->solve;
答案 0 :(得分:8)
如何编写可扩展代码?
有计划。假设你正在编写一个用于绘制a的算法 一套点。你需要这些点的来源,一个绘图的地方 它们,以及用于插入不在其中的点的算法 集。
(只是一个注释,假设“图形”在这里表示“图表”,而不是 离散数学意义上的图表。)
让我们定义代表这些操作的角色。的来源 积分必须能够为我们提供要点:
package Graphomatic::PointSource;
use Moose::Role;
requires 'get_points'; # return a list of points
1;
绘图仪必须允许我们绘制一个点:
package Graphomatic::Plot;
use Moose::Role;
requires 'plot_point'; # plot a point
requires 'show_graph'; # show the final graph
1;
当给出两个附近的点时,插值器必须给出一个点:
package Graphomatic::Interpolate;
use Moose::Role;
requires 'interpolate_point';
1;
现在,我们只需要根据这些来编写我们的主要应用程序 作用:
package Graphomatic;
use Moose;
use Graphomatic::PointSource;
use Graphomatic::Plot;
use Graphomatic::Interpolate;
has 'source' => (
is => 'ro',
does => 'Graphomatic::PointSource',
handles => 'Graphomatic::PointSource',
required => 1,
);
has 'plot' => (
is => 'ro',
does => 'Graphomatic::Plot',
handles => 'Graphomatic::Plot',
required => 1,
);
has 'interpolate' => (
is => 'ro',
does => 'Graphomatic::Interpolate',
handles => 'Graphomatic::Interpolate',
required => 1,
);
sub run { # actually render and display the graph
my $self = shift;
my @points = $self->get_points; # delegated from the PointSource
for my $x (some minimum .. some maximum) {
my ($a, $b) = nearest_points( $x, @points );
$self->plot_point( $self->interpolate_point($a, $b, $x) );
}
$self->show_graph;
}
1;
现在定义一些源实现是一件简单的事情。 让我们从文件中读取点:
package Graphomatic::PointSource::File;
use Moose;
use MooseX::FileAttribute;
# ensure, at compile-time, that this class is a valid point
# source
with 'Graphomatic::PointSource';
has_file 'dataset' => ( must_exist => 1, required => 1 );
sub get_points {
my $self = shift;
return parse $self->dataset->slurp;
}
1;
并绘制到Z窗口系统:
package Graphomatic::Plot::Z;
use Moose;
use Z;
with 'Graphomatic::Plot';
has 'window' => ( is => 'ro', isa => 'Z::Window', lazy_build => 1);
sub _build_window { return Z->new_window }
sub plot_point {
my ($self, $point) = @_;
$self->window->plot_me_a_point_kthx($point->x, $point->y);
}
sub show_plot {
my $self = shift;
$self->window->show;
}
1;
用随机数生成器进行插值(嘿,我很懒,我就是 不要查找双三次插值:P):
package Graphomatic::Interpolate::Random;
use Moose;
with 'Graphomatic::Interpolate';
sub interpolate_point {
my ($self, $a, $b, $x) = @_;
return 4; # chosen by fair dice roll.
# guaranteed to be random.
}
1;
现在我们可以将所有部分组合成一个工作程序:
use Graphomatic::PointSource::File;
use Graphomatic::Plot::Z;
use Graphomatic::Interpolate::Random;
my $graphomatic = Graphomatic->new(
source => Graphomatic::PointSource::File->new(
file => 'data.dat',
),
plot => Graphomatic::Plot::Z->new,
interpolate => Graphomatic::Interpolate::Random->new,
);
$graphomatic->run;
现在,您可以干净地自定义任何部件而不会影响 其他部分,只需实现“做”所需的新类 角色。 (如果他们说'有......'但他们不符合要求, 加载课程后,您将收到错误消息。如果你试着 使用一个实例作为一个不“做”正确角色的参数 构造函数会死。
输入安全性,这是一件很棒的事情。)
至于处理配置文件,只是以某种方式读取名称和参数, 然后:
my $interpolate_class = get_config('interpolate_class');
Class::MOP::load_class($interpolate_class);
my $interpolate = $interpolate_class->new( %interpolate_class_args );
my $graphomatic = Graphomatic->new( interpolate => $interpolate, ... );
MooseX::YAML是一种很好的自动化方法。
答案 1 :(得分:3)
Module::Pluggable可能会帮助您实现自己想要的目标。
答案 2 :(得分:3)
结帐Moose和MooseX::Types::LoadableClass
package MyClass;
use Moose;
use MooseX::Types::LoadableClass qw/ClassName/;
has 'algo' => (
is => 'ro'
, isa => ClassName
, coerce => 1
);
sub solve {
my $self = shift;
my $algo = $self->algo->new;
# stuff using algo
}
## These work:
Graph::ShortestPathGraph->new({ algo => 'Graph::DFS' })->solve;
Graph::ShortestPathGraph->new({ algo => 'Graph::BFS' })->solve;
## As does this:
my $gspg = Graph::ShortestPathGraph->new;
$gspg->algo('Graph::BFS');
$gspg->solve;
如果该类不存在,则抛出错误。但是,如果您想自己创建Algo课程,那么制作它们Traits可能要好得多。
使用Moose解决这个问题有很多预先制定的解决方案,请关注CPAN。
答案 3 :(得分:1)
这是一个简单的自制示例。一旦你理解了它,你就可以继续使用那些旨在解决这类问题的模块:
#!/usr/bin/perl
package Me::Mine;
use base 'Class::Accessor';
__PACKAGE__->mk_accessors( qw( dumper ) );
sub dump {
my $self = shift;
my $dumper = $self->dumper;
eval "require $dumper";
print "Dumping using $dumper\n", $dumper->Dump([ $self ]);
return;
}
package main;
use strict; use warnings;
my $me = Me::Mine->new;
my $you = Me::Mine->new;
$me->dumper('Data::Dumper');
$you->dumper('YAML');
$_->dump for $me, $you;
输出:
Dumping using Data::Dumper $VAR1 = bless( { 'dumper' => 'Data::Dumper' }, 'Me::Mine' ); Dumping using YAML --- YAML --- - !!perl/hash:Me::Mine dumper: YAML