我正在嘗試多重繼承,并希望在不使用諸如Moose解決幕后問題之類的包的情況下掌握它的竅門。
我有兩個基類,Left并且Right,在一個“破碎的”菱形中:
Left Right
\ /
Multi
他們都實作了overloadfor ""。當perform在任何基類中呼叫名為 named 的方法時,這些方法應該使用此多載來列印物件該部分的表示。Multi實作perform如下:
sub perform {
my $self = shift;
$self->Left::perform;
$self->Right::perform;
}
發生的情況是,兩個基類perform方法都按預期呼叫,但是當這些方法呼叫任何其他方法(如""多載)時,它將始終是Left. 但是,如果Right單獨創建的實體(不是作為 的一部分Multi),它將呼叫正確的方法。
- 我想知道如何在這種情況下讓一個方法選擇它自己的包的方法而不是它最左邊的兄弟基類的方法?
這是我嘗試過的(在 perl v5.26.1 和 v5.32.1):
#!/usr/bin/perl
use strict;
use warnings;
package Left; #----------------------------------------------------------------
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my $self = shift;
$self->{_leftval} = shift;
return $self;
}
sub value { shift->{_leftval}; }
use overload '""' => sub {
my $self = shift;
'Left(' . $self->value . ')';
};
sub perform {
my $self = shift;
print '# LEFT ' . $self . "\n";
}
package Right; #---------------------------------------------------------------
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my $self = shift;
$self->{_rightval} = shift;
return $self;
}
sub value { shift->{_rightval}; }
use overload '""' => sub {
my $self = shift;
'Right(' . $self->value . ')';
};
sub perform {
my $self = shift;
print '# RIGHT ' . $self . "\n";
}
package Multi; #---------------------------------------------------------------
use parent -norequire, 'Left', 'Right' ;
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my $self = shift;
$self->Left::_init(shift);
$self->Right::_init(shift);
return $self;
}
sub perform {
my $self = shift;
$self->Left::perform;
$self->Right::perform;
}
package main; #----------------------------------------------------------------
my $l = Left->new("a Left");
my $r = Right->new("a Right");
my $m = Multi->new("lEfT", "rIgHt");
$l->perform;
$r->perform;
print "---- and now a Multi ----\n";
$m->perform;
預期輸出:
# LEFT Left(a Left)
# RIGHT Right(a Right)
---- and now a Multi ----
# LEFT Left(lEfT)
# RIGHT Right(rIgHt)
實際輸出(注意最后一行):
# LEFT Left(a Left)
# RIGHT Right(a Right)
---- and now a Multi ----
# LEFT Left(lEfT)
# RIGHT Left(lEfT)
uj5u.com熱心網友回復:
虛擬方法是基于物件的類而不是呼叫者的命名空間/包/類來決議的。
在 Perl 中,一個方法是否是虛擬的并不是一個內在屬性。這取決于如何稱呼它。
$o->method # Virtual method
避免這種情況需要指定類
$o->Class::method
您不想要虛方法呼叫,因此您需要更改呼叫方法的方式。
package Left;
use overload '""' => \&to_string;
sub to_string {
my $self = shift;
'Left(' . $self->value . ')';
}
sub perform {
my $self = shift;
print '# LEFT ' . $self->Left::to_string() . "\n";
}
除了那都是錯的。如果這是一種方法,這將是有道理的。但我們正在談論每一種方法。這是紅旗的空襲警報。
我們在這里想要的是組合,而不是繼承。Multi IS A Left 和 IS A Right 不正確。相反,Multi HAS A Left 和 HAS A Right。
package Multi; #---------------------------------------------------------------
use parent -norequire, 'Left', 'Right' ;
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my $self = shift;
$self->{ left } = Left ->new(shift);
$self->{ right } = Right->new(shift);
return $self;
}
sub perform {
my $self = shift;
$self->{ left }->perform;
$self->{ right }->perform;
}
uj5u.com熱心網友回復:
我想知道如何在這種情況下使方法選擇自己的包的方法
$self像這樣rebless 有幫助嗎:
package Right;
# [...]
sub perform {
my $self = shift;
if (ref $self ne "Right") {
bless $self, "Right";
}
print '# RIGHT ' . $self . "\n";
}
uj5u.com熱心網友回復:
這是建立在H?kon's answer 的基礎上的。bless $self, 'Right'在Right::perform有效地似乎打破了繼承。$m->perform直接呼叫的第二個呼叫Right::perform-Multi::perform甚至沒有被呼叫。
作為對此的解決方法,我添加了一個祝福背景關系類,它對創建和破壞都有祝福。我必須在所有可能呼叫另一個包中的任何方法的方法中創建這些背景關系之一。
package Reblesser; #-----------------------------------------------------------
sub new {
my $class = shift;
my $self = bless {
object => shift,
class => shift
}, $class;
$self->rebless;
$self;
}
sub rebless {
my $self = shift;
bless $self->{object}, $self->{class} if(ref $self->{object} ne $self->{class});
}
sub DESTROY {
shift->rebless;
}
現在Left::perform變成:
sub perform {
my $self = shift;
my $ctx = Reblesser->new($self, __PACKAGE__);
print '# LEFT ' . $self . "\n";
}
Right::perform:
sub perform {
my $self = shift;
my $ctx = Reblesser->new($self, __PACKAGE__);
print '# RIGHT ' . $self . "\n";
}
Multi::perform:
sub perform {
my $self = shift;
my $ctx = Reblesser->new($self, __PACKAGE__);
$self->Left::perform;
$self->Right::perform;
}
輸出(即使有多個$m->perform呼叫):
# LEFT Left(a Left)
# RIGHT Right(a Right)
---- and now a Multi ----
# LEFT Left(lEfT)
# RIGHT Right(rIgHt)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/366774.html
