2010-03-19 14 views
5

Làm cách nào để viết các đối tượng Perl có mã mở rộng? Im suy nghĩ trình điều khiển, hoặc một số cấu hình, nơi người dùng có thể vượt qua trong một chuỗi "Classname :: Class" hoặc một cái gì đó. Cảm ơn.Làm cách nào để viết đối tượng Perl bằng các plugin?

Ví dụ, đối với một lớp đồ thị:

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

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

Vui lòng cung cấp ví dụ chi tiết hơn về những gì bạn đang cố giải quyết. Một số mã giả sẽ tốt. –

+0

đã thêm, thx aloot – Timmy

Trả lời

8

Làm thế nào để viết mã mở rộng?

Với quy hoạch. Giả sử bạn đang viết một thuật toán để vẽ một tập hợp các điểm . Bạn cần một nguồn của những điểm đó, một nơi để vẽ sơ đồ chúng và một thuật toán cho các điểm nội suy không nằm trong tập hợp .

(Và chỉ là một lưu ý, cho rằng "đồ thị" có nghĩa là "biểu đồ" ở đây, chứ không phải là một đồ thị theo nghĩa toán học rời rạc.)

Hãy xác định vai trò đại diện cho những người hoạt động. Một nguồn tin của điểm phải có khả năng cung cấp cho chúng với các điểm:

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

requires 'get_points'; # return a list of points 

1; 

Một plotter phải cho phép chúng ta vẽ một điểm:

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

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

1; 

Và một interpolater phải cung cấp cho chúng ta một điểm khi đưa ra hai điểm lân cận:

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

requires 'interpolate_point'; 

1; 

Bây giờ, chúng ta chỉ cần viết ứng dụng chính của chúng tôi về những vai trò:

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; 

Bây giờ, việc xác định một số triển khai nguồn là vấn đề đơn giản. Hãy đọc điểm từ một tập tin:

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; 

Và mưu với hệ thống cửa sổ 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; 

Và suy với một bộ tạo số ngẫu nhiên (hey, tôi lười biếng, và tôi sẽ không nhìn lên bicubic suy: 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; 

Bây giờ chúng ta có thể lắp ráp tất cả các mảnh vào một chương trình làm việc:

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; 

Bây giờ bạn có thể tùy chỉnh sạch bất kỳ phần nào mà không ảnh hưởng đến các phần khác, chỉ cần thực hiện các lớp mới "làm" yêu cầu vai trò. (Nếu họ nói 'với ...' và họ không đáp ứng các yêu cầu, bạn sẽ gặp lỗi ngay sau khi bạn tải lớp. Nếu bạn cố gắng sử dụng một ví dụ làm tham số không "làm "vai trò đúng, các nhà xây dựng sẽ chết

Loại an toàn, đó là một điều tuyệt vời)

đối với xử lý tập tin cấu hình, chỉ cần đọc tên và các thông số bằng cách nào đó, và sau đó:..

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 là cách hay để tự động hóa việc này.

+2

Rất độc đáo. – daotoad

+0

@daotoad: thanks :) – jrockway

1

Đây là một ví dụ đơn giản về nhà. Một khi bạn hiểu nó, bạn có thể chuyển sang các module được thiết kế để có những cảm giác mệt mỏi của các loại điều này:

#!/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; 

Output:

Dumping using Data::Dumper 
$VAR1 = bless({ 
       'dumper' => 'Data::Dumper' 
       }, 'Me::Mine'); 
Dumping using YAML 
--- YAML 
--- 
- !!perl/hash:Me::Mine 
    dumper: YAML
+2

Đừng làm 'eval" yêu cầu $ dumper "'. Điều đó làm tôi buồn rầu. Không quá khó để chuyển đổi tên mô-đun thành tên tệp của nó. Thậm chí có một mục CPAN tiện dụng cho nó: http://search.cpan.org/~mattlaw/Module-Util/lib/Module/Util.pm#module_path – friedo

+1

@friedo Bạn có thể mở rộng trên đó không? Ý tôi là, đây là mã throwaway chỉ để minh họa một kỹ thuật để không có kiểm tra lỗi/tham số, nhưng điều gì đặc biệt sai với 'eval 'yêu cầu $ dumper" '(như được khuyến cáo bởi http://perldoc.perl.org/ function/require.html? –

+1

Tôi có một sự dị ứng (có lẽ không hợp lý) đối với chuỗi 'eval' trong tất cả các dạng của nó, trừ khi nó hoàn toàn cần thiết cho một cái gì đó. Trong trường hợp này, nó không cần thiết, bởi vì nó dễ dàng lấy tên tệp, vì vậy bạn có thể sử dụng '$ mod_file = module_path $ dumper của tôi, yêu cầu $ mod_file;'. – friedo

3

Thanh toán Moose, và 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; 

Nếu lớp không tồn tại, một lỗi được ném. Tuy nhiên, nếu bạn muốn tự mình tạo các lớp Algo, có lẽ bạn nên làm tốt hơn cho chúng Traits.

Có rất nhiều giải pháp được tạo trước bằng Moose để giải quyết vấn đề này, nhìn xung quanh trên CPAN.

Các vấn đề liên quan