从PHP的父上下文中访问父级的重写方法

时间:2012-12-14 08:20:51

标签: php class

我有一个名为 ClassA 的绘图PHP类,它由许多其他绘图类扩展,例如 ClassB

我需要继承的类来激活其父类的 Draw()方法。但是,在我的特殊情况下,我不想直接调用这种方法(例如:parent::Draw())。我想要第三个函数(例如:parent::InvokeDraw())从父元素的上下文中调用我的绘图方法。

以下是一些代码来说明:

class ClassA
{
    function Draw()
    {

        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        $this->Draw();
    }
}

class ClassB extends ClassA
{
    function Draw()
    {
        parent::InvokeDraw();

        /* Drawing code ... */

    }
}

我面临的问题是 InvokeDraw()不会调用父 Draw()方法,而是扩展类'自己的 Draw( )方法,从而导致无限循环。

虽然问题相当合理,但我很难找到解决方法。如何完成这项任务?

期望的效果

chart

无限循环问题

chart

4 个答案:

答案 0 :(得分:4)

在肤浅的层面上,继承就像合并代码一样。继承的方法是子类的一部分,就好像它们是类的本机一样:

class A {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'bar';
    }

}

class B extends A { }

对于所有意图和目的,这相当于写作:

class B {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'bar';
    }

}

班级B的功能为foobar,就好像它们是宣言的一部分一样。

子类中的覆盖方法将一个特定的实现替换为另一个:

class B extends A {

    public function bar() {
        echo 'baz';
    }

}

这就好像B被声明为:

class B {

    public function foo() {
        echo 'foo';
    }

    public function bar() {
        echo 'baz';
    }

}

有一个例外:使用parent关键字可以获得父方法的实现方法。它不会改变代码执行的上下文,只是使用父方法的实现方法:

class B extends A {

    public function bar() {        public function bar() {
        parent::bar();      --->       echo 'bar';
    }                              }

}

它在B的当前实例的相同上下文中工作,它只是将父代的旧代码拉出以太。

如果在父方法中调用另一个方法,它将在子级的上下文中执行,而不是在父类的上下文中执行。因为您尚未实例化父级,所以您正在与一个孩子一起工作。要演示属性(它与方法的作用相同):

class A {

    protected $value = 'bar';

    public function bar() {
        echo $this->value;
    }

}

class B extends A {

    protected $value = 'baz';

    public function bar() {
        parent::bar();     // outputs "baz"
        echo $this->value; // outputs "baz"
    }

}

因此,您的问题无法解决。您无法在“父级上下文”中调用方法。您可以在当前上下文中调用父代码,这就是全部。你正在创建的是一个简单的循环,因为代码是否是继承无关紧要,它们都在同一个上下文中工作。

您需要重新设计该类,以便不在循环中调用方法。

答案 1 :(得分:2)

正如deceze所说,除非你重新设计课程,否则你的问题无法解决。

如果您使用的是PHP 5.4,则可以使用Traits

创建2个特征,每个特征都有自己的Draw函数。然后在父母中使用一个特征,在孩子中使用另一个特征。

然后,为了从子代访问父函数,请将InvokeDraw别名设置为父Draw函数。

以下是一个例子:

<?php

trait DrawingA
{
    function Draw()
    {
        echo 'I am the parent';
    }
}

trait DrawingB
{
    function Draw()
    {
        echo 'I am the child';
    }
}

class ClassA
{
    use DrawingA;
}

class ClassB extends ClassA
{
    use DrawingA, DrawingB {
        DrawingB::Draw insteadof DrawingA;
        DrawingA::Draw as InvokeDraw;
    }
}

$b = new ClassB();

echo 'Child: ',  $b->Draw(), PHP_EOL;
echo 'Parent: ', $b->InvokeDraw() . PHP_EOL;

/*
Outputs:

Child: I am the child
Parent: I am the parent

*/

答案 2 :(得分:1)

这是使用静态方法

<?php

class ClassA
{
    function Draw()
    {
        echo "DrawA";
        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        self::Draw();
    }
}

class ClassB extends ClassA
{
    function Draw()
    {
        echo "DrawB";
        parent::InvokeDraw();

        /* Drawing code ... */

    }
}

echo "Begin:<br>";

$cb = new ClassB();
$cb->Draw();

请注意,我唯一更改的是InvokeDraw()方法并使其使用引用类的self,而不是$this

中的对象

输出:

Begin:
DrawBDrawA

修改 要回答下面的评论,我将简要介绍代码的工作原理以及代码的工作原理。

您的代码中会发生什么:

  1. 我们创建B并开始使用
  2. 我们在B->Draw()班级和B对象内工作时致电B
  3. B->Draw()静态调用(这意味着类方法)来自A类 BUT A::Invoke()我们仍在使用B个对象。
  4. 静态调用A::Invoke()调用$this->Draw();,当我们正在使用B对象时,$this指的是ClassB的实例。
  5. 我们在这里循环。
  6. 上面的代码会发生什么:

    1. 我们创建B并开始使用
    2. 我们在B->Draw()班级和B对象内工作时致电B
    3. B->Draw()从A类 BUT 以及代码中静态调用(表示类方法)A::Invoke,我们仍在使用B个对象。< / LI>
    4. 静态调用A::Invoke()调用self::Draw(),它与ClassA::Draw()基本相同,因为它是一个静态方法,我们不关心我们当前使用的是什么对象,我们称之为A的{​​{1}}方法。
    5. Draw()方法可以根据需要执行。
    6. 我将在第二个答案中为代码提供相同的解释,它不使用静态调用:

      1. 我们创建A::Draw()并开始使用
      2. 我们在B班级和B->Draw()对象内工作时致电B
      3. B 创建 B->Draw()的实例。
      4. A调用B->Draw(),这意味着我们开始使用的对象是类A->Invoke()的实例,而不是之前的A
      5. 此时我们完全忘记了B甚至存在且仅适用于B

        1. A调用A->Invoke(),这意味着我们正在调用$this->Draw(),因为我们已经使用了类A->Draw()的实例。
        2. A按预期执行。
        3. 从可用性的角度来看,我们可以看到静态方法更好,因为我们可以定义一些静态属性,并且可以在A->Draw()执行时使用它们。如果我们使用非静态方法,那么我们需要在方法的参数中传递我们需要的数据。

          我希望这说清楚。上面的序列没有正确的术语,但它是故意编写的,我认为通过这种方式理解流程更容易。

答案 3 :(得分:0)

我非常喜欢你的问题,我研究了一下,并试着像我的第一个答案那样做,但没有静态电话:

<?php

class ClassA
{

    function Draw()
    {
        echo "DrawA";
        /* Drawing code ... */

    }

    function InvokeDraw()
    {
        $this->Draw();
    }
}

class ClassB extends ClassA
{

    function Draw()
    {
        echo "DrawB";
        $x = new parent;
        $x->InvokeDraw();

        /* Drawing code ... */

    }
}

echo "Begin:<br>";

$cb = new ClassB();
$cb->Draw();

输出:

Begin:
DrawBDrawA