13

我有一个名为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()方法,从而导致无限循环。

尽管这个问题相当合乎逻辑,但我很难找到解决方法。如何完成这项任务?

想要的效果

图表

无限循环问题

图表

4

4 回答 4

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"
    }

}

因此,您的问题没有解决方案。您不能在“父上下文”中调用方法。您可以在当前上下文中调用父级的代码,仅此而已。您正在创建的是一个简单的循环,因为代码是否被继承并不重要,它都在相同的上下文中工作。

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

于 2012-12-14T09:18:37.000 回答
2

这个是使用静态方法

<?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类 ANDB对象中工作时调用。
  3. B->Draw()A::Invoke()从类 A静态调用(这意味着类方法)我们仍在使用B对象。
  4. 静态调用A::Invoke()调用$this->Draw();,当我们当前使用B对象时,$this引用ClassB.
  5. 在这里,我们开始循环。

上面的代码发生了什么:

  1. 我们创建B并开始使用它
  2. 我们B->Draw()B类 ANDB对象中工作时调用。
  3. B->Draw()A::Invoke从类 A静态调用(这意味着类方法) ,在您的代码中,我们仍在使用B对象。
  4. 静态调用与A::Invoke()调用self::Draw()基本相同ClassA::Draw(),因为它是一个静态方法,所以我们不关心我们当前使用的对象是什么,我们调用A'Draw()方法。
  5. A::Draw()方法按照我们的需要执行。

我将在我的第二个答案中为代码提供相同的解释,它不使用静态调用:

  1. 我们创建B并开始使用它
  2. 我们B->Draw()B类 ANDB对象中工作时调用。
  3. B->Draw() 创建A. _
  4. B->Draw()调用A->Invoke(),这意味着我们开始使用一个对象,它是类的实例,A而不是B像以前那样。

在这一点上,我们完全忘记了B甚至存在并且只与A

  1. A->Invoke()调用$this->Draw()这意味着我们正在调用A->Draw(),因为我们已经使用了类的实例A
  2. A->Draw()按照我们的预期执行。

从可用性的角度来看,我们可以看到静态方法更好,因为我们可以定义一些静态属性,您可以在A::Draw()执行时使用它们。如果我们使用非静态方法,那么我们需要在方法的参数中传递我们需要的数据。

我希望这可以说清楚。上面的序列没有正确的术语,但它是故意写的,我认为这样更容易理解流程。

于 2012-12-14T08:36:35.770 回答
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

*/
于 2012-12-14T10:35:47.440 回答
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
于 2012-12-14T08:52:07.840 回答