2010-10-28 19 views
4

我正在使用一些Perl包,我們將其稱爲Some::ParserSome::Data。 A Some::Parser對象具有返回類型爲Some::Data的對象的方法。我已經寫了一個課程,擴展了Some::Data課程,我們稱之爲My::DataMy::Data類的對象實際上只是Some::Data類的對象,但使用了更易於使用的其他方法。如何重新分類Perl對象

我的問題是,我想繼續使用Some::Parser類來解析數據。正如我剛纔所說,Some::Parser對象給我Some::Data對象。一旦我手上有一個Some::Data對象,是否有任何方法將它重新分類爲My::Data對象?我將如何做到這一點?

我完全願意改變我的方法,假設有人可以提出一個更好的方式來做我想做的事情,但是編寫我自己的解析器並不是我想做的事情!

回答

10

這味道有點像雜碎。現在可能是重新考慮你的戰略的時候了。例如,也許你應該寫My::Parser,它返回My::Data對象。

但是,如果你不想做,你可以手動使用bless改變一個對象的類:

my $obj = Some::Data->new; 
bless $obj, 'My::Data'; 

見的perldoc bless

+0

由於我還處於早期階段,現在是重新考慮我的策略的最佳時機......這就是爲什麼我要問了!我一直在閱讀很多關於祝福的文章,而且人們一直在警告重新出現對象。如果新的對象類擴展了原始類,這會不會成爲一個問題? – 2010-10-28 21:58:45

+0

我認爲延長原來的課程可能是最好的方式。 (只要原始類很容易擴展,如果這是一個小問題,不要發瘋,重新祝福可以解決它)。你可以在你的派生類中編寫自己的構造函數,它可以保存對象。你的構造函數可能需要先調用父類的構造函數,然後重新祝福,這相當於同樣的事情,但提供了一個更好的接口。 – friedo 2010-10-28 22:06:05

4

你可以重新bless什麼。

在Perl 5中的繼承無非是搜索@ISA

3

您可以重新bless返回的對象無論你的心臟的慾望:

#!/usr/bin/perl 

package Some::Data; 
use strict; use warnings; 

sub new { my $class = shift; bless { @_ } => $class } 

sub a { $_[0]->{a} } 

package My::Data; 
use strict; use warnings; 
use base 'Some::Data'; 

sub a_squared { 
    my $self = shift; 
    my $v = $self->a; 
    return $v * $v; 
} 

package Some::Parser; 
use strict; use warnings; 

sub new { my $class = shift; bless { @_ } => $class } 

sub parse { return Some::Data->new(a => 3) } 

package main; 

use strict; use warnings; 

my $data = Some::Parser->new->parse; 
bless $data => 'My::Data'; 

printf "%.1f\t%.1f\n", $data->a, $data->a_squared; 

或者,您可以使用@軍事審判的想法:

#!/usr/bin/perl 

package Some::Data; 
use strict; use warnings; 

sub new { my $class = shift; bless { @_ } => $class } 

sub a { $_[0]->{a} } 

package My::Data; 
use strict; use warnings; 
use base 'Some::Data'; 

sub a_squared { 
    my $self = shift; 
    my $v = $self->a; 
    return $v * $v; 
} 

package Some::Parser; 
use strict; use warnings; 

sub new { my $class = shift; bless { @_ } => $class } 

sub parse { 
    my $self = shift; 
    return $self->data_class->new(a => 3); 
} 

sub data_class { $_[0]->{data_class} } 

package main; 

use strict; use warnings; 

my $data = Some::Parser->new(data_class => 'My::Data')->parse; 
printf "%.1f\t%.1f\n", $data->a, $data->a_squared; 
7

大概來處理這樣的事情最好的辦法是爲Some::Parser提供一種方法來指定它應該用於數據對象的類。例如,HTML::TreeBuilder提供了element_class方法。如果您希望TreeBuilder生成除HTML::Element節點以外的其他節點,則可以繼承HTML::TreeBuilder並覆蓋element_class以返回所需的節點類。 (TreeBuilder中的實際代碼稍微複雜一點,因爲在HTML-Tree 4之前有一個不同的機制來做這件事,而新的維護者不想破壞它。)

我認爲你沒有寫Some::Parser,但也許它已經具備了這個能力。如果不是,它的維護者可能會接受一個補丁。這應該是一個相當簡單的改變。您只需添加一個data_class方法(sub data_class { 'Some::Data' }),然後將Some::Data->new更改爲$self->data_class->new。然後,您可以子類Some::Parser創建My::Parser,並覆蓋data_class

+0

這個答案在子類化'Path :: Class :: *'的時候指出了我的正確方向,其中Path :: Class :: Dir可以返回Path :: Class :: File對象,反之亦然。由於這兩個模塊的優秀設計,我可以覆蓋相應模塊中的'file_class()'和'dir_class()'方法。 – Mike 2012-05-20 09:20:45

1

我會考慮重新冒險冒險。一旦你有了一個對象,你就無法真正知道它是否是使用它的構造函數創建的(通常是Foo :: new()),或者某個人重新爲其他對象賦予了它。

的問題是,有些構造函數是脂肪,這意味着他們做一個整體洛塔不僅僅是祝福更多的東西:

sub new { 
    my $pkg = shift; 
    my ($required) = @_; 
    croak "Bad call" unless defined $required; 
    _do_something_magic ($required); 
    my $self = { 'foo' => $required }; 
    return bless $self, $pkg; 
} 

在這種情況下,你再祝福的對象可能不是你所期望的一個後來在代碼中。

人們可能會考慮內置「重新祝福」功能的構造函數。但是這樣的「對象轉換器」會使設計變得更加複雜。

堅持基本定義:「一個對象是類的一個實例。永遠。」。