【问题标题】:Perl: How to call a specific method in multiple inheritance?Perl:如何在多重继承中调用特定方法?
【发布时间】:2017-04-12 06:26:36
【问题描述】:

我在 perl 中有一个包,它使用另外两个包作为其基础。

父母1:

package Parent1;

use strict;
use warnings;

sub foo
{
    my $self = shift;
    print ("\n Foo from Parent 1 ");
    $self->baz();
}

sub baz
{
    my $self = shift;
    print ("\n Baz from Parent 1 ");
}
1;

父母 2:

package Parent2;

use strict;
use warnings;

sub foo
{
    my $self = shift;
    print ("\n Foo from Parent 2 ");
    $self->baz();
}

sub baz
{
    my $self = shift;
    print ("\n Baz from Parent 2 ");
}
1;

孩子: 这里用到了上面两个父包。

package Child;

use strict;
use warnings;

use base qw(Parent1);
use base qw(Parent2);

sub new
{
    my $class = shift;
    my $object = {};
    bless $object,$class;
    return $object;
}
1;

主要:

use strict;
use warnings;
use Child;

my $childObj = new Child;

$childObj->Parent2::foo();

输出:

 Foo from Parent 2 
 Baz from Parent 1 

我的分析:

从输出中可以清楚地看出,子对象被传递给 parent2 的 foo 方法,并且从该 foo 方法中,它正在调用 baz 方法。它首先检查 Child 包中的 baz 方法,因为我用子对象调用它。由于 baz 方法不在子包中,它会检查基类中的方法。 Parent1 是 Child 的第一个基类。于是,它找到了Parent1中的方法,并调用了Parent1的baz方法。

我的问题:

是否可以在不改变child中基类顺序的情况下调用Parent2的baz方法?

我的预期输出:

 Foo from Parent 2 
 Baz from Parent 2

上面的例子只是我实际问题的一个类比。我无权修改基类。我只能修改 Child 类。那么,是否可以更改子类,使其从 Parent2 中获取两种方法而不更改基类的顺序?

谢谢!

【问题讨论】:

    标签: perl inheritance package subroutine


    【解决方案1】:

    如果您有权修改基类,您可以通过将$self->baz() 更改为Parent2::baz($self) 来做到这一点,但您说您不能这样做。

    既然这不是一个选项,您对临时更改基类的顺序有何看法?在 Perl 中,基类列表实际上只是每个包中名为 @ISA 的数组,因此您可以使用 local 在块中创建该数组的本地化副本:

    #!/usr/bin/env perl    
    
    use strict;
    use warnings;
    use 5.010;
    
    package Parent1;
    
    sub foo { say 'foo1'; $_[0]->baz; }
    sub baz { say 'baz1'; }
    
    package Parent2;
    
    sub foo { say 'foo2'; $_[0]->baz; }
    sub baz { say 'baz2'; }
    
    package Child;
    
    use base qw( Parent1 Parent2 );
    
    sub new { return bless {} }
    
    package main;
    
    my $childobj = Child->new;
    {
      local @Child::ISA = qw( Parent2 Parent1 );
      say 'In localized block';
      $childobj->Parent2::foo;
    }
    
    say 'Block has exited';
    $childobj->Parent2::foo;
    

    输出:

    In localized block
    foo2
    baz2
    Block has exited
    foo2
    baz1
    

    因此您可以看到,这在块内提供了您想要的输出,带有本地化的@ISA,然后在块退出时恢复原始行为。

    另外,结束时的附注:在 Perl 中使用 new Child 称为“间接对象表示法”,通常被认为是 Bad Thing。我建议改用Child->new

    【讨论】:

      【解决方案2】:

      您可以覆盖 Child 类中所需的方法,调度到完全限定的调用

      package Child;
      
      ...
      
      sub baz 
      {
          shift;
          Parent2::baz(@_);
      }
      
      ...
      

      将此添加到您的代码中,它会根据需要打印,并带有Baz from Parent2

      这是一种相当手动的方式来“增加”这些类的接口,这显然不是为多重继承而设计的。但是您描述的情况确实令人不快,必须做这样的事情,或者对特定的@ISA 操作进行硬编码。

      对于更复杂的需求,请参阅mro,与所有这些明确相关。它支持一些内省,因此您可以在运行时做出决定。您仍然必须拥有Child::baz()

      你能改变设计并且使用多重继承吗?

      【讨论】:

      • 谢谢 Zdim。我不可能改变设计。这就是我试图寻找其他可能的解决方案的原因。
      • @jayaganthan 好的,我预料到了。您可能必须进入那里并硬编码何时调用谁。手动调度这些呼叫。我认为main 无法通知foo 调用哪个baz(不更改@ISA)。
      【解决方案3】:

      您可以为首选方法创建别名。通常这是一个坏主意,除非您知道自己在做什么。看看perldoc perlmod #Symbol Tables

      将您的主文件更新为:

       use strict;
       use warnings;
       use Child;
      
       my $childObj = new Child;
      
       $childObj->Parent2::foo();
      
       print "------\n";
       no warnings; # Otherwise will complain about 'Name "Child::baz" used only once: possible typo'
       # Create an alias
       *Child::baz = *Parent2::baz;
       use warnings;
       $childObj->Parent2::foo();
      

      输出

      Foo from Parent 2
      Baz from Parent 1
      ------
      Foo from Parent 2
      Baz from Parent 2
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-05
        • 1970-01-01
        • 2013-12-25
        • 1970-01-01
        • 1970-01-01
        • 2020-07-03
        相关资源
        最近更新 更多