如何使用插件编写 Perl 对象? [英] How do I write Perl object with plugins?

查看:41
本文介绍了如何使用插件编写 Perl 对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何用可扩展代码编写 Perl 对象?我在想驱动程序或一些配置,用户可以在其中传递字符串Classname::Class"或其他东西.谢谢.

例如,对于图类:

my $spg = Graph::ShortestPathGraph->new;$spg->算法(Graph::DFS");$spg->解决;$spg->算法( "Graph::BFS" );$spg->解决;

解决方案

如何编写可扩展的代码?

有计划.假设您正在编写一个算法来绘制一个点集.你需要这些点的来源,一个绘图的地方它们,以及一种用于插入不在其中的点的算法设置.

(只是一个注释,假设图形"在这里的意思是图表",而不是离散数学意义上的图.)

让我们定义代表这些操作的角色.一个来源积分必须能够为我们提供积分:

 包Graphomatic::PointSource;使用 Moose::Role;需要get_points";# 返回点列表1;

绘图仪必须允许我们绘制一个点:

 包Graphomatic::Plot;使用 Moose::Role;需要'plot_point';# 绘制一个点需要show_graph";# 显示最终图形1;

当给定两个附近的点时,插值器必须给我们一个点:

 包Graphomatic::Interpolate;使用 Moose::Role;需要 'interpolate_point';1;

现在,我们只需要根据这些来编写我们的主要应用程序角色:

 封装Graphomatic;使用驼鹿;使用 Graphomatic::PointSource;使用Graphomatic::Plot;使用Graphomatic::Interpolate;有来源"=>(是 =>'罗',确实=>'图形::点源',句柄 =>'图形::点源',需要 =>1、);有情节"=>(是 =>'罗',确实=>'图形::情节',句柄 =>'图形::情节',需要 =>1、);有插值"=>(是 =>'罗',确实=>'图形::插值',句柄 =>'图形::插值',需要 =>1、);sub run { # 实际渲染和显示图形我的 $self = shift;我的@points = $self->get_points;# 从 PointSource 委托对于我的 $x(最低限度.. 最高限度){我的($a,$b)=nearest_points($x,@points);$self->plot_point( $self->interpolate_point($a, $b, $x) );}$self->show_graph;}1;

现在是定义一些源实现的简单问题.让我们从文件中读取点:

包Graphomatic::PointSource::File;使用驼鹿;使用 MooseX::FileAttribute;# 在编译时确保这个类是一个有效的点# 来源使用 'Graphomatic::PointSource';has_file '数据集' =>( must_exist => 1, required => 1 );子 get_points {我的 $self = shift;返回解析 $self->dataset->slurp;}1;

并绘制到 Z 窗口系统:

包Graphomatic::Plot::Z;使用驼鹿;使用 Z;使用'Graphomatic::Plot';有窗口"=>( is => 'ro', isa => 'Z::Window', lazy_build => 1);sub _build_window { return Z->new_window }子情节_点{我的 ($self, $point) = @_;$self->window->plot_me_a_point_kthx($point->x, $point->y);}子 show_plot {我的 $self = shift;$self->window->show;}1;

并使用随机数生成器进行插值(嘿,我很懒,而且我不打算查找双三次插值 :P):

包Graphomatic::Interpolate::Random;使用驼鹿;使用'Graphomatic::Interpolate';子插值点{我的 ($self, $a, $b, $x) = @_;返回 4;# 通过公平掷骰子选择.# 保证是随机的.}1;

现在我们可以将所有部分组装成一个工作程序:

使用Graphomatic::PointSource::File;使用Graphomatic::Plot::Z;使用Graphomatic::Interpolate::Random;我的 $graphomatic = Graphomatic->new(来源 =>Graphomatic::PointSource::File->new(文件 =>'数据.dat',),情节=>图形::情节:: Z->新,插值 =>图形::插值::随机->新,);$graphomatic->run;

现在您可以干净地自定义任何部分,而不会影响其他部分,只需实现执行"所需的新类角色.(如果他们说'with ...'并且他们不符合要求,加载类后,您将立即收到错误消息.如果你尝试使用一个实例作为参数,它没有做"正确的角色,构造函数会死.

类型安全,这是一件很棒的事情.)

至于处理配置文件,只需以某种方式读取名称和参数,然后:

my $interpolate_class = get_config('interpolate_class');Class::MOP::load_class($interpolate_class);我的 $interpolate = $interpolate_class->new(%interpolate_class_args);我的 $graphomatic = Graphomatic->new( interpolate => $interpolate, ... );

MooseX::YAML 是实现自动化的好方法.>

How do I write Perl objects with extensible code? Im thinking drivers, or some configuation, where the user can pass in a string "Classname::Class" or something. Thanks.

For example, for a graph class:

my $spg = Graph::ShortestPathGraph->new;
$spg->Algorithm( "Graph::DFS" );
$spg->solve;

$spg->Algorithm( "Graph::BFS" );
$spg->solve;

解决方案

How do I write extensible code?

With planning. Let's say you are writing an algorithm for plotting a set of points. You need a source of those points, a place to plot them, and an algorithm for interpolating points that aren't in the set.

(And just a note, assume that "graph" means "chart" here, rather than a graph in the discrete math sense.)

Let's define roles that represent those operations. A source of points must be able to provide us with the points:

 package Graphomatic::PointSource;
 use Moose::Role;

 requires 'get_points'; # return a list of points

 1;

A plotter must allow us to plot a point:

 package Graphomatic::Plot;
 use Moose::Role;

 requires 'plot_point'; # plot a point
 requires 'show_graph'; # show the final graph

 1;

And an interpolater must give us a point when given two nearby points:

 package Graphomatic::Interpolate;
 use Moose::Role;

 requires 'interpolate_point';

 1;

Now, we just need to write our main application in terms of these roles:

 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;

Now it's a simple matter of defining some source implementations. Let's read points from a file:

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;

And plot to the Z window system:

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;

And interpolate with a random number generator (hey, I'm lazy, and I'm not going to look up bicubic interpolation :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;

Now we can assemble all the pieces into a working program:

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;

Now you can cleanly customize any of the parts without affecting the other parts, simply by implementing new classes that "do" the required roles. (If they say 'with ...' and they don't meet the requirements, you will get an error as soon as you load the class. If you try to use an instance as a parameter that doesn't "do" the right role, the constructor will die.

Type safety, it's a wonderful thing.)

As for handling config files, just read names and parameters somehow, and then:

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 is a nice way of automating this.

这篇关于如何使用插件编写 Perl 对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆