简体   繁体   English

Perl 继承 - 子程序覆盖

[英]Perl inheritance - Subroutine override

Is there a way to override sub a in class_1_1 and class_2_1 with a new behavior(same for both classes) without adding the overridden method to both class_1_1 and class_2_1 ?有没有办法用新行为(两个类相同)覆盖class_1_1class_2_1 sub a而不将覆盖的方法添加到class_1_1class_2_1

package class_0
    sub a
    sub b
1;

package class_1 
    use parent 'class_0'
    sub b
1;

package class_2
    use parent 'class_0'
    sub b
1;

package class_1_1
    use parent 'class_1'
1;

package class_2_1
    use parent 'class_2'
1;  

One tailor-made solution is to add a role for the wanted behavior.一种量身定制的解决方案是为所需行为添加角色

Here is an example with native OO code, using stand-alone Role::Tiny for roles这是一个带有本机 OO 代码的示例,对角色使用独立的Role::Tiny

use warnings;
use strict;

use SomeClass;

my $obj = SomeClass->new;

$obj->a_method;

The class SomeClass.pmSomeClass.pm

package SomeClass;

use warnings;
use strict;
use feature 'say';

use Role::Tiny::With;  # To "consume" a role. Comes with Role::Tiny

# Consume roles from "AddedRoles.pm"
# That may add or require methods, or override the ones here
with 'AddedRoles';     

sub new { bless { }, $_[0]] }

sub a_method { say "From ", __PACKAGE__ }

1;

This role can be added to other classes as well, by adding the use statement and the line with 'AddedRoles';通过添加use语句和with 'AddedRoles';的行with 'AddedRoles';也可以将此角色添加到其他类with 'AddedRoles'; to them, regardless of their inheritance relationships.对他们来说,不管他们的继承关系如何。 See the docs for ways to fine tune the process.有关微调过程的方法,请参阅文档。

The package with the roles, AddedRoles.pm包含角色的包, AddedRoles.pm

package AddedRoles;

use feature 'say';  # we get strict and warnings from the package but not this

use Role::Tiny;

# Require that consumers implement methods; Add a method
#require method_1, method_2;    
#sub added_method { say "Adding functionality to consumers" }

# Replace "a_method" in a consumer
around a_method => sub { say "From an overriding role ", __PACKAGE__ } 

1;

The Role::Tiny need be installed (which has no dependencies).需要安装Role::Tiny (没有依赖项)。 To replace a method that is defined in the class that consumes the role we need a method modifier around , provided by Class::Method::Modifiers , so that is an additional dependency.要替换在使用角色的类中定义的方法,我们需要一个方法修饰符around , Class::Method::Modifiers 提供,因此这是一个额外的依赖项。

Roles are often compared to inheritance, and claimed to provide a nicer and lighter alternative since inheritance is normally "baked" into the whole class hierarchy etc. However, inheritance generally specializes behavior while roles are clearly more flexible;角色经常被比作继承,并声称提供了一个更好、更轻松的替代方案,因为继承通常“烘焙”到整个类层次结构等中。然而,继承通常专门化行为,而角色显然更灵活; they add or modify behavior (or can specialize for that matter).他们添加或修改行为(或可以专门针对该问题)。 I rather see roles fitting nicely somewhere between inheritance and composition.我宁愿看到角色在继承和组合之间很好地匹配。

Note that with roles we can have a near equivalent of multiple inheritance, with almost none of its formidable (and forbidding) headaches.请注意,对于角色,我们可以拥有几乎等同于多重继承,几乎没有它令人生畏(和令人生畏)的麻烦。

The bare-bones example above demonstrates the use of Role::Tiny on its own.上面的简单示例演示了Role::Tiny的使用。 But roles are better utilized along with Moose or Moo frameworks, using Moose::Role or Moo:Role .但是使用Moose::RoleMoo:Role可以更好地利用角色与MooseMoo框架一起使用。

I would absolutely recommend to look into these frameworks.我绝对建议研究这些框架。 I strongly believe in the value of learning well how to use the Perl's native OO system, but once one has that under their belt it'd be a shame to not try Moose or Moo .我坚信学习如何使用 Perl 的原生 OO 系统的价值,但是一旦有人掌握了这一点,不尝试MooseMoo将是一种耻辱。


In this particular question though the method to override is inherited from another class and in that case there is no need for a modifier. 在这个特定问题中,虽然要覆盖的方法是从另一个类继承的,但在这种情况下不需要修饰符。 From "Role Composition" in docs来自文档中的“角色构成”

If a method is already defined on a class, that method will not be composed in from the role.如果一个方法已经在一个类上定义,该方法将不会从角色中组合。 A method inherited by a class gets overridden by the role's method of the same name, though.但是,由类继承的方法会被角色的同名方法覆盖。

So in the case of this question it is enough to normally define a sub in the roles package所以在这个问题的情况下,通常在角色包中定义一个子就足够了

# In the package that defines roles (like 'AddedRoles' above)
sub a_method  { say "From an overriding role ", __PACKAGE__ } 

and when this role is consumed by a class which inherits a_method , like class_1_1 in the question, the method does get overridden by this one.当这个角色被一个继承a_method的类消耗时,比如class_1_1中的class_1_1 ,该方法确实被这个方法覆盖。

Note that if a method is defined in the class itself (not inherited) then a role defined as a normal sub is quietly ignored (it won't override the method and it won't warn or such).请注意,如果在类本身中定义了一个方法(不是继承的),那么定义为普通sub的角色将被悄悄忽略(它不会覆盖该方法,也不会发出警告等)。

On the other hand, around overrides a method in either case (inherited or defined in the class), but cannot add a method that isn't there at all (and throws an exception if that is attempted).另一方面,在任何一种情况下, around覆盖一个方法(在类中继承或定义),但不能添加根本不存在的方法(如果尝试,则会抛出异常)。

You can do it with multiple inheritance:您可以使用多重继承来做到这一点:

#!/usr/bin/perl

use strict;
use warnings;

package class_0;
    sub new {
        my ($class, $foo) = @_;
        return bless{foo=>$foo}, $class;
    }

    sub a {
        my $self=shift;
        print 'a class_0 foo=' . $self->{foo} . "\n";
    }

    sub b {
        my $self=shift;
        print 'b class_0 foo=' . $self->{foo} . "\n";
    }

package new_a;
    sub a {
        my $self=shift;
        print 'a new_a foo=' . $self->{foo} . "\n";
    }

package class_1;
    use parent -norequire, 'class_0';
    sub b {
        my $self=shift;
        print 'b class_1 foo=' . $self->{foo} . "\n";
    }

package class_1_1;
    use parent -norequire, 'new_a', 'class_1';

package class_2;
    use parent -norequire, 'class_0';
    sub b {
        my $self=shift;
        print 'b class_2 foo=' . $self->{foo} . "\n";
    }

package class_2_1;
    use parent -norequire, 'new_a', 'class_2';

package main;

# example usage
my $c0 = class_0->new(1);
my $c1 = class_1->new(2);
my $c2 = class_2->new(3);
my $c11 = class_1_1->new(4);
my $c21 = class_2_1->new(5);

print "-- a class_0 b class_0\n";
$c0->a; $c0->b;
print "-- a class_0 b class_1\n";
$c1->a; $c1->b;
print "-- a class_0 b class_2\n";
$c2->a; $c2->b;
print "-- a new_a b class_1\n";
$c11->a; $c11->b;
print "-- a new_a b class_2\n";
$c21->a; $c21->b;

Output:输出:

-- a class_0 b class_0
a class_0 foo=1
b class_0 foo=1
-- a class_0 b class_1
a class_0 foo=2
b class_1 foo=2
-- a class_0 b class_2
a class_0 foo=3
b class_2 foo=3
-- a new_a b class_1
a new_a foo=4
b class_1 foo=4
-- a new_a b class_2
a new_a foo=5
b class_2 foo=5

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM