2013-07-04 42 views
3

我已經寫了一些Perl代碼,它們構成了基本固有的兩個類。我想這將打印像這樣爲什麼perl對象實例互相覆蓋

Mik: Meow! Meow! 
Sat: Woof! Woof! 

但實際打印這種方式:

Sat: Woof! Woof! 
Sat: Woof! Woof! 

package Animal; 
sub new { 

    my $obj = shift; 
    my $name = shift; 
    our %pkg = ('name' => $name); 
    bless \%pkg, $obj; 
    return \%pkg; 
} 

package Cat; 
@ISA = ("Animal"); 

sub new { 
    my $obj = shift; 
    my $name = shift; 
    my $self = $obj->SUPER::new($name); 
    return $self; 
} 

sub get_name { 
    my $obj = shift; 
    return $obj->{'name'}; 
} 


sub talk { 
    my $obj = shift; 
    return "Meow! Meow!"; 
} 

package Dog; 
@ISA = ("Animal"); 

sub new { 
    my $obj = shift; 
    my $name = shift; 
    my $self = $obj->SUPER::new($name); 
    return $self; 
} 

sub get_name { 
    my $obj = shift; 
    return $obj->{'name'}; 
} 

sub talk { 
    my $obj = shift; 
    return "Woof! Woof!"; 
} 

package Main; 

my $cat = new Cat('Mike'); 
my $dog = new Dog('Sat'); 

print $cat->get_name() . ": " . $cat->talk() , "\n"; 
print $dog->get_name() . ": " . $dog->talk() , "\n"; 

但如果我改變這樣的調用者,它打印什麼我想是。所以在$dog實例化之後爲什麼$cat對象被覆蓋很奇怪?

package Main; 

my $cat = new Cat('Mily'); 
print $cat->get_name() . ": " . $cat->talk() , "\n"; 

my $dog = new Dog('Sat'); 
print $dog->get_name() . ": " . $dog->talk() , "\n"; 

回答

8

爲什麼你要成爲一個全局變量?構造函數更改爲:

sub new { 
    my $obj = shift; 
    my $name = shift; 
    my %pkg = ('name' => $name); 
    bless \%pkg, $obj; 
    return \%pkg; 
} 

更重要的是,改變它的東西更地道:

sub new { 
    my $class = shift; 
    my $name = shift; 
    my $self = { name => $name }; 
    return bless $self, $class; 
} 

繼續前進:

爲什麼實現各種動物的newget_name?這兩種方法都可以繼承。雖然我們在這,我們不妨擺脫掉與@ISA亂搞:

package Animal; 
sub new { 
    my $class = shift; 
    my $name = shift; 
    my $self = { name => $name }; 
    return bless $self, $class; 
} 

sub get_name { 
    my $self = shift; 
    return $self->{'name'}; 
} 

package Cat; 
use base qw/ Animal /; 

sub talk { 
    my $self = shift; 
    return "Meow! Meow!"; 
} 

package Dog; 
use base qw/ Animal /; 

sub talk { 
    my $self = shift; 
    return "Woof! Woof!"; 
} 

package Main; 

my $cat = Cat->new('Mike'); 
my $dog = Dog->new('Sat'); 

print $cat->get_name() . ": " . $cat->talk() , "\n"; 
print $dog->get_name() . ": " . $dog->talk() , "\n"; 

請問該教程或預定你下面?

雖然上面是完全沒有問題,你不妨做它的現代Perl的方式:

package Animal; 
use Moose; 
has name => (required => 1, is => 'rw', isa => 'Str'); 

package Cat; 
use Moose; 
extends 'Animal'; 

has talk => (default => "Meow! Meow!", is => 'ro'); 

package Dog; 
use Moose; 
extends 'Animal'; 

has talk => (default => "Woof! Woof!", is => 'ro'); 

package Main; 
my $cat = Cat->new(name => 'Mike'); 
my $dog = Dog->new(name => 'Sat'); 

print $cat->name . ": " . $cat->talk , "\n"; 
print $dog->name . ": " . $dog->talk , "\n"; 
+1

嗯,是的。良好的OO設計總是很難;語言能做的最好的不是妨礙你的行爲。但我認爲Moose可以更容易地感受到設計的正確性。以上面的例子。'talk'是一種方法,但它實際上只是一個屬性。因此它應該是基類中的一個方法,它在子類中使用另一個屬性。我發現很難以舊式的形式看到這樣的東西。 – innaM

+0

@innaM非常感謝您的幫助。讓我解釋一下。 1.我真的很困惑,我的Dog實例覆蓋了我的貓,所以我想查看每個類的細節。我並不是要在每個類中定義get_name。 2.我曾經使用過很多很老的Unix機器,所以我必須在Perl 5.8中編寫代碼,並且必須避免寫出比它更新的任何東西。 :-(。3.特別是對於這個問題,它是我在網上某處遇到的一個Python程序,我想將它翻譯成Perl,但不幸的是,它在其上滑落。 –

2

一言以蔽之:our聲明包變量,所以每次執行our %pkg = (...),您分配一個新的值到同一個變量。由於所有\%pkg引用指向相同的變量,所有返回值new是相同的對象。參考只能祝福到一個班級,所以最後一個獲勝。

只需將our更改爲my,它應該按預期工作。

4

你聲明的變量使用

our %pkg 

這是一個單一的數據結構(%Animal::pkg)的別名來存儲實例數據,因此所有對象都使用相同的哈希值。將our更改爲my,以便每次都創建一個新的散列。


這可能是值得一提的是,在Perl的「由內而外」的對象可以和你使用一個共享的數據結構在包存儲實例數據,但使這項工作需要抽象出新的水平,我不會推薦他們開始OO Perl,他們是一種後天的口味。