如何覆盖特征函数并从被覆盖的函数中调用它?

Posted

技术标签:

【中文标题】如何覆盖特征函数并从被覆盖的函数中调用它?【英文标题】:How to override trait function and call it from the overridden function? 【发布时间】:2012-08-09 23:15:54 【问题描述】:

场景:

trait A 
    function calc($v) 
        return $v+1;
    


class MyClass 
    use A;

    function calc($v) 
        $v++;
        return A::calc($v);
    


print (new MyClass())->calc(2); // should print 4

这段代码不起作用,我找不到像继承一样调用特征函数的方法。我试过打电话给self::calc($v)static::calc($v)parent::calc($v)A::calc($v) 和以下:

trait A 
    function calc($v) 
        return $v+1;
    


class MyClass 
    use A 
        calc as traitcalc;
    

    function calc($v) 
        $v++;
        return traitcalc($v);
    

没有任何作用。

有没有办法让它工作,或者我必须完全覆盖比这更复杂的特征函数:)

【问题讨论】:

【参考方案1】:

你的最后一个就快到了:

trait A 
    function calc($v) 
        return $v+1;
    


class MyClass 
    use A 
        calc as protected traitcalc;
    

    function calc($v) 
        $v++;
        return $this->traitcalc($v);
    

特征不是一个类。您不能直接访问其成员。它基本上只是自动复制和粘贴...

【讨论】:

只是为了澄清——一旦你的类定义了相同的方法,它会自动覆盖特征的。正如@ircmaxell 提到的,当它为空时,该特征会填充方法。 @PhillipWhelan 如果您可以添加有关“无法按预期工作”的更多信息,那就太好了。这样写对理解会出现什么样的错误行为没有多大帮助,也不能向我们保证这不是您的暂时错误。也许有一些关于你正在谈论的问题的问题? (最终)谢谢。 问题是特征中的所有其他方法都不再包括在内。 仅供参考:如果您的特征函数是静态的,您可以通过调用A::calc(1)访问该函数 正如 Phillip 提到的(我认为),您将如何为一种特征的一种方法执行此操作,同时仍然像正常一样包含具有相同特征的所有其他方法?最好不要明确引用每种方法。【参考方案2】:

如果类直接实现方法,则不会使用traits版本。也许你在想的是:

trait A 
    function calc($v) 
        return $v+1;
    


class MyClass 
    function calc($v) 
        return $v+2;
    


class MyChildClass extends MyClass


class MyTraitChildClass extends MyClass
    use A;


print (new MyChildClass())->calc(2); // will print 4

print (new MyTraitChildClass())->calc(2); // will print 3

因为子类不直接实现该方法,所以如果有其他使用父类的方法,它们将首先使用该特征的方法。

如果你愿意,特征可以使用父类中的方法(假设你知道方法会在那里)例如

trait A 
    function calc($v) 
        return parent::calc($v*3);
    

// .... other code from above
print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2)

您还可以提供覆盖方法,但仍按如下方式访问 trait 方法:

trait A 
    function trait_calc($v) 
        return $v*3;
    


class MyClass 
    function calc($v) 
        return $v+2;
    



class MyTraitChildClass extends MyClass
    use A 
      A::trait_calc as calc;
    



class MySecondTraitChildClass extends MyClass
    use A 
      A::trait_calc as calc;
    

    public function calc($v) 
      return $this->trait_calc($v)+.5;
    



print (new MyTraitChildClass())->calc(2); // will print 6
echo "\n";
print (new MySecondTraitChildClass())->calc(2); // will print 6.5

你可以在http://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5看到它的工作原理

【讨论】:

【参考方案3】:

如果有兴趣,另一种方法 - 使用额外的中间类来使用正常的 OOO 方式。这简化了 parent::methodname

的使用
trait A 
    function calc($v) 
        return $v+1;
    


// an intermediate class that just uses the trait
class IntClass 
    use A;


// an extended class from IntClass
class MyClass extends IntClass 
    function calc($v) 
        $v++;
        return parent::calc($v);
    

【讨论】:

这种方法将消除您使用traits 所拥有的任何优势。就像在多个类中组合多个特征(例如,一个类中的特征 A、B、另一个类中的特征 B、C、D,另一个类中的特征 A、C 等等) 不,使用这种方法,您仍然拥有拥有特质的优势。您可以在 IntClass 中使用此特性,但如果您愿意,也可以在许多其他类中使用它。如果仅在 IntClass 中使用 Trait,它将毫无用处。在这种情况下,最好将 calc() 方法直接放在该类中。 这完全不适合我。 ScreenablePerson::save() 存在,Candidate 使用 Validating 特征并扩展 ScreenablePerson,并且所有三个类都有 save() 我同意这很有趣。它有效,因为对于扩展类,特征方法实际上存在于父类中。我个人认为,如果您发现自己处于这种情况会很方便,但我不建议您按设计这样做。【参考方案4】:

使用另一个特征:

trait ATrait 
    function calc($v) 
        return $v+1;
    


class A 
    use ATrait;


trait BTrait 
    function calc($v) 
        $v++;
        return parent::calc($v);
    


class B extends A 
    use BTrait;


print (new B())->calc(2); // should print 4

【讨论】:

【参考方案5】:

另一种变体:在 trait 中定义两个函数,一个执行实际任务的受保护函数,另一个调用受保护函数的公共函数。

这只是让类在想要覆盖函数时不必弄乱“use”语句,因为它们仍然可以在内部调用受保护的函数。

trait A 
    protected function traitcalc($v) 
        return $v+1;
    

    function calc($v) 
        return $this->traitcalc($v);
    


class MyClass 
    use A;

    function calc($v) 
        $v++;
        return $this->traitcalc($v);
    


class MyOtherClass 
    use A;



print (new MyClass())->calc(2); // will print 4

print (new MyOtherClass())->calc(2); // will print 3

【讨论】:

以上是关于如何覆盖特征函数并从被覆盖的函数中调用它?的主要内容,如果未能解决你的问题,请参考以下文章

Backbone:调用扩展视图的覆盖渲染()函数

覆写(Override)和重载(Overload)的比较

如何从被调用函数“继续”调用函数中的循环?

构造函数中可覆盖的方法调用有啥问题?

如何从基类函数调用覆盖的虚函数?

QSqlQueryModel - 覆盖未调用的函数数据