Perl Moose родительский класс с дочерью - PullRequest
4 голосов
/ 03 января 2011
package Point;
use Moose;

has 'x' => (isa => 'Int', is => 'rw');
has 'y' => (isa => 'Int', is => 'rw');

package Point3D;
use Moose;

extends 'Point';

has 'z' => (isa => 'Int', is => 'rw');

package main;

use Data::Dumper;

my $point1 = Point->new(x => 5, y => 7);
my $point3d = Point3D->new(z => -5);

$point3d = $point1;
print Dumper($point3d);

Можно ли привести родительский класс к дочернему классу, например с ++? В моем тесте $ point3d теперь является Point, а не Point3D включает Point.

Ответы [ 4 ]

5 голосов
/ 03 января 2011

Взгляните на документацию Class :: MOP по CPAN , особенно методы clone_object и rebless_instance:

sub to_3d {
  my ($self, %args) = @_;
  return Point3D->meta->rebless_instance(
    $self->meta->clone_object($self),
    %args,
  );
}

, а затем используйте ее следующим образом:

my $point_3d = $point->to_3d(z => 7);

Это также позаботится о том, чтобы обработать только что указанные %args, как если бы они были переданы конструктором.Например, во время этой сборки учитываются компоновщики, значения по умолчанию и ограничения типов.

2 голосов
/ 03 января 2011

Вам не нужно приводить в Perl, так как это динамический язык.Однако в вашем случае переменная $point3d содержит ссылку на объект Point в конце скрипта.Вы не можете рассматривать это как Point3D , потому что это не Point3D. Вы можете вручную преобразовать это, но вы не можете "переделать" существующий объект как другойучебный класс.(Ну, теоретически вы можете в Perl, но не должны.)

1 голос
/ 03 января 2011

Ну, Dumper должен сказать вам, что $point3d теперь Point, а не Point3D, потому что ваше присвоение $point3d = $point1 делает $point3d второй ссылкой на тот же объект, что и $point1 , Экземпляр Point3D, на который изначально ссылалась $point3d, теперь теряется в пространстве с повторным счетом 0, что делает его пригодным для сбора мусора.

Как сказал cdhowie, вы на самом деле не делаете типотипирование в Perl так, как в C / C ++. Самое близкое, о чем я могу подумать, - это прибегнуть к соглашению о вызовах не-OO и использовать, например, Point3D::z($point1, 4), чтобы дать $point1 z-index 4, но это довольно неуклюже, и вам придется использовать тот же синтаксис для любых будущих ссылок на его z-index. Также обратите внимание, что при использовании этого соглашения о вызовах Point3D должен фактически определять метод z [1], иначе вы получите ошибку времени выполнения, потому что наследование не работает, когда вы делаете это таким образом потому что вы ссылаетесь на Point3D как на пакет, а не на $point1 как на объект.

Если вы действительно хотите превратить Point в Point3D, вы можете легко изменить фактический тип объекта, используя bless (та же самая команда, используемая для преобразования простой ссылки в объект в первую очередь). , хотя это скрыто внутри Moose в вашем примере кода), но я подозреваю, что ручная отбрасывание объекта Moose вызвало бы гнев Moose. (Но если это так, я уверен, что Moose предоставляет более безопасный способ изменения класса объекта. Я просто недостаточно использую Moose, чтобы знать, что это будет.)

[1] ... или AUTOLOAD, но это совсем другая банка червей.

0 голосов
/ 03 января 2011

По сути, я поддерживаю слова cdhowie и Дейва С., но добавлю еще одну вещь.

Если вы действительно хотите превратить $point3d, который содержит объект класса Point, в реальный объект подкласса Point3D, то правильным способом OO является создание конструктора new_from_Point() в Point3D class, который принимает объект класса Point в качестве входных данных и создает объект Point3D (вероятно, он должен принимать дополнительный параметр "z"). Эквивалентом C ++ будет конструктор с сигнатурой (const Point &, double &z=0.0)

...