美文网首页程序员Perl小推车
第九章 管理真实的程序(六) -UNIVERSAL包

第九章 管理真实的程序(六) -UNIVERSAL包

作者: 可以没名字吗 | 来源:发表于2016-03-30 09:35 被阅读85次

    UNIVERSAL包

    Perl内部的UNIVERSAL包是其他所有包的祖先---以面向对象的视角来看那就是终极父类。UNIVERSAL提供了一些方法可供使用、继承和重写。

    VERSION()方法

    VERSION()方法会返回调用者(包或类)里的变量$VERSION的值。如果你提供了版本号作为参数且比调用者里$VERSION的值还要大,那就会产生一个异常。

    假设模块HowlerMonkey的版本是1.23(也就是$VERSION的值是1.23),那么VERSION()的行为是这样的:

    my $hm = HowlerMonkey->new;
    
    say HowlerMonkey->VERSION; # prints 1.23
    say $hm->VERSION; # prints 1.23
    say $hm->VERSION( 0.0 ); # prints 1.23
    say $hm->VERSION( 1.23 ); # prints 1.23
    say $hm->VERSION( 2.0 ); # exception!
    

    重写VERSION()方法要慎重,因为几乎不存在要重写的理由。

    DOES()方法

    DOES()方法是用来进行角色机制判断的,参数是调用者和角色名,如果调用者实现了该角色就返回真值(角色实现的机制可以有很多,如继承、委托、组成、角色应用或其他机制)。

    默认DOES()的实现机制就是通过isa()方法,因为继承(isa()用于继承)就是这样一个机制:一个类实现了一个角色。假设给定一个类Cappuchin,那么它的DOES()行为可能是下面这样:

    say Cappuchin->DOES( 'Monkey' ); # prints 1
    say $cappy->DOES( 'Monkey' ); # prints 1
    say Cappuchin->DOES( 'Invertebrate' ); # prints 0
    

    某些情况下你可能会需要重写DOES()方法。

    can()方法

    can()方法用来判断调用者是否具有某方法。参数为方法名字符串,如果调用者具有该方法则返回该方法引用,否则就返回假值。你可以在类、对象、包上调用can()方法。

    给定一个SpiderMonkey类,并且类中有screech方法:

    if (my $meth = SpiderMonkey->can( 'screech' )) {...}
    #将方法引用赋值给$meth
    

    这样就能实现一种模式:调度方法前先检查该方法是否存在:

    if (my $meth = $sm->can( 'screech' )
    {
    # method; not a function
    $sm->$meth();
    }
    

    使用can()方法来测试一个包是否实现了一个指定的函数或方法:

    use Class::Load;
    
    die "Couldn't load $module!" unless load_class( $module );
    
    if (my $register = $module->can( 'register' ))
    {
    # function; not a method
    $register->();
    }
    

    Module::Pluggable

    通过使用CPAN模块Class::Load能方便地进行模块加载。Module::Pluggable则能让你很容易地构建出插件管理系统。

    isa()方法

    isa()方法接受一个字符串,可以是类名或类型名(SCALAR, ARRAY, HASH, Regexp, IO,CODE)。可以把它当成类方法或实例方法来使用,如果调用者派生于或就是给定类(或调用者就是指定类型的bless过的引用)则返回真。

    给定一个对象$pepper(一个哈希引用,类Monkey的神圣引用,继承自Mammal类),isa()方法的行为是这样的:

    say $pepper->isa( 'Monkey' ); # prints 1
    say $pepper->isa( 'Mammal' ); # prints 1
    say $pepper->isa( 'HASH' ); # prints 1
    say Monkey->isa( 'Mammal' ); # prints 1
    
    say $pepper->isa( 'Dolphin' ); # prints 0
    say $pepper->isa( 'ARRAY' ); # prints 0
    say Monkey->isa( 'HASH' ); # prints 0
    

    任何类都可以重写isa()方法。isa()方法有时候非常有用,比如如跟某些模块(Test::MockObject和Test::MockModule)协同工作时,或者代码没有使用角色时。要明白的是通常具有有充分的理由才会重写该方法。

    类是否存在?
    UNIVERSAL::isa()和UNIVERSAL::can()功能类似,你可以使用后者来安全地判断类是否存在,若UNIVERSAL::can($classname, 'can' )返回真,说明在某个地方定义了$classname类,但这个类存在却有可能不可用。

    扩展UNIVERSAL

    有个想法很有吸引力:将其他的方法也放到UNIVERSAL中,这样所有其他的类和对象中就都能使用这些方法了。但是,别这样做,特别是当代码不是你自己写的或者不是由你自己维护的时候,因为全局性的行为往往有着微妙的副作用。

    偶尔借助UNIVERSAL来帮助调试修正代码还是可以的。例如Joshua benJore的UNIVERSAL::ref分发包就让几乎没用的ref()操作符变得有用。
    UNIVERSAL::can和UNIVERSAL::isa分发包能帮助你调试多态相关的BUG。Perl::Critic也可以帮助你解决一些问题。

    除非及其小心且有着实在而充分的理由,否则不应该将代码放到UNIVERSAL里面。

    相关文章

      网友评论

        本文标题:第九章 管理真实的程序(六) -UNIVERSAL包

        本文链接:https://www.haomeiwen.com/subject/bmzrlttx.html