【问题标题】:Calling base constructor in perl在 perl 中调用基本构造函数
【发布时间】:2011-07-12 02:48:28
【问题描述】:

在 Perl 中从类构造函数调用基构造函数的正确方法是什么?

我见过这样的语法:

 my $class = shift; 
 my $a = shift; 
 my $b = shift;
 my $self = $class->SUPER::new($a, $b);
 return $self;

这是正确的吗?如果我们有几个父类怎么办。例如这样的类:

 package Gamma;
 use base Alpha;
 use base Beta;

 sub new
 {
   # Call base constructors...
 }
 1;

【问题讨论】:

    标签: perl oop constructor base


    【解决方案1】:

    如果您的构造函数所做的只是调用父构造函数(如在您的示例中,您根本不需要编写一个。只需将其省略即可调用父构造函数;您只需要确保对象被祝福为正确的类型:

    package Parent;
    use strict;
    use warnings;
    
    sub new
    {
        my ($class, @args) = @_;
    
        # do something with @args
    
        return bless {}, $class;
    }
    1;
    

    如果您使用上面的代码并且有一个用use parent 'Parent'; 声明的Child 类,那么Parent 构造函数将正确地构造一个child。

    如果您需要在 Child 中添加一些属性,那么您所拥有的基本上是正确的:

    package Child;
    use strict;
    use warnings;
    
    use parent 'Parent';
    
    sub new
    {
        my ($class, @args) = @_;
    
        # possibly call Parent->new(@args) first
        my $self = $class->SUPER::new(@args);
    
        # do something else with @args
    
        # no need to rebless $self, if the Parent already blessed properly
        return $self;
    }
    1;
    

    但是,当您将多重继承引入混合时,需要在每个步骤中决定正确的做法。这意味着每个类都有一个自定义构造函数,它决定如何将 Parent1 和 Parent2 的属性合并到子类中,然后最终将生成的对象祝福到 Child 类中。这种复杂性是多重继承是一个糟糕的设计选择的众多原因之一。您是否考虑过重新设计对象层次结构,可能通过将一些属性移动到角色中?此外,您可能希望使用对象框架来处理一些繁忙的工作,例如Moose。现在很少需要编写自定义构造函数了。

    (最后,您应该避免使用变量 $a$b;它们在 Perl 中的处理方式不同,因为它们是排序函数和其他一些内置函数中使用的变量。)

    【讨论】:

      【解决方案2】:

      这个问题就是为什么有些人建议不要在你的new 方法中做任何有趣的事情。 new 应该创建并返回一个祝福引用,很难让一个系统为来自不同父类的同一个对象处理两次。

      一个更简洁的选择是拥有一个只创建对象并调用另一个可以设置对象的方法的新方法。第二种方法的行为方式可以允许调用多个父方法。实际上new 是你的分配器,而这个其他方法是你的构造函数。

      package Mother;
      use strict;
      use warnings;
      
      sub new {
          my ($class, @args) = @_;
          my $self = bless {}, $class;
          return $self->_init(@args);
      }
      
      sub _init {
          my ($self, @args) = @_;
      
          # do something
      
          return $self;
      }
      
      package Father;
      use strict;
      use warnings;
      
      sub new {
          my ($class, @args) = @_;
          my $self = bless {}, $class;
          return $self->_init(@args);
      }
      
      sub _init {
          my ($self, @args) = @_;
      
          # do something else
      
          return $self;
      }
      
      package Child;
      use strict;
      use warnings;
      
      use base qw(Mother Father);
      
      sub _init {
          my ($self, @args) = @_;
      
          # do any thing that needs to be done before calling base classes
      
          $self->Mother::_init(@args); # Call Mother::_init explicitly, SUPER::_init would also call Mother::_init
          $self->Father::_init(@args); # Call Father::_init explicitly, SUPER::_init would NOT call Father::_init
      
          # do any thing that needs to be done after calling base classes
      
          return $self;
      }
      

      关于使用多重继承可能会遇到的复杂情况,Ether 是正确的。你仍然需要知道Father::_init 不会覆盖Mother::_init 所做的任何决定。从那里调试只会变得更加复杂和困难。

      我会赞同Moose 的建议,它的界面比我上面的示例更好地分离了对象创建和初始化,这通常是可行的。

      【讨论】:

      • 谢谢,这似乎是将我当前的代码转换为有效代码的最简单方法。
      【解决方案3】:

      当使用多重继承时,默认的方法解析顺序是低于标准的。我强烈建议您添加

      use mro 'c3';
      

      到模块,并使用调用链中的下一个构造函数

      sub new {
         my ($class) = @_;
         return $class->next::method(@_);
      }
      

      Alpha 和 Beta 必须这样做才能使其正常工作。

      参考:mro

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-12
        • 1970-01-01
        • 2017-12-08
        • 1970-01-01
        • 1970-01-01
        • 2018-07-21
        相关资源
        最近更新 更多