2013-07-04 2 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"; 

Но если изменить вызывающему таким способом, он печатает что Должен быть. Так что довольно странно, почему объект $cat был перезаписан после того, как был создан экземпляр $dog?

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; 
} 

Перемещение по:

Почему реализовать new и get_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"; 

Могу ли я спросить, какой учебник или книгу, вы следуете?

Хотя выше прекрасно, вы могли бы также сделать это так, как Modern 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 всегда тяжелый; лучшее, что может сделать язык, не мешает вам. Но я думаю, что Муз облегчает восприятие правильности вашего дизайна. Возьмите пример выше.'talk' используется метод, но это действительно только атрибут. Таким образом, это должен быть метод в базовом классе, который использует еще один атрибут в дочерних классах. Мне тяжелее видеть такие вещи в старомодной форме. – innaM

+0

@innaM Большое спасибо за вашу помощь. Позвольте мне кое-что объяснить. 1. Я действительно запутался, что мой экземпляр Dog переписал мой Cat, чтобы я хотел проверить данные в каждом классе. Я действительно не хочу определять get_name в каждом классе. 2. Раньше я работал с очень старыми машинами Unix, поэтому мне приходилось записывать код в Perl 5.8 и избегать писать что-либо более новое. :-(. 3. В частности, для этого вопроса, это была программа Python, которую я встретил где-то в Сети, и я бы хотел перевести ее на Perl. Но, к сожалению, на нее набросилось. –

2

В двух словах: our объявляет переменные пакета, поэтому каждый раз, когда our %pkg = (...) выполняется, то присвоить новое значение к той же переменной. Поскольку все ссылки \%pkg указывают на один и тот же var, все возвращаемые значения new являются одним и тем же объектом. Ссылка может быть только благословлена ​​в один класс, поэтому побеждает последний.

Просто измените our на my, и он должен работать должным образом.

4

Вы объявили переменную для хранения данных экземпляра с помощью

our %pkg 

Это псевдоним для одной структуры данных (%Animal::pkg), так что все ваши объекты используют один и тот же хэш. Измените our на my, чтобы создать новый хэш каждый раз.


Может быть, стоит отметить, что «внутри-аут» объекты в Perl могут и использовать общую структуру данных в пакете для хранения данных экземпляра, но есть дополнительный уровень абстракции требуется, чтобы сделать эту работу , и я бы не рекомендовал начинать с OO Perl с ними, они приобрели вкус.

Смежные вопросы