【问题标题】:Access parent's overridden method from parent's context in PHP从 PHP 中的父级上下文访问父级的覆盖方法
【发布时间】:2012-12-14 08:20:50
【问题描述】:

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

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

想要的效果

无限循环问题

【问题讨论】:

  • 为什么要通过invokeDraw?该方法还有其他有趣的作用吗?
  • 为了简单起见,这是一个设计选择。在真实案例场景中,Draw($a, $b, $c, $d, $e) 接受 5 个参数,我不希望用户看到上面 2 行中填充了相同的 5 个参数的parent::Draw($a, $b, $c, $d, $e)。它看起来杂乱无章,效率低下。我的尝试是简化代码美学。

标签: php class


【解决方案1】:

从表面上看,继承就像合并代码一样。继承的方法是子类的一部分,就好像它们是该类的原生方法:

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

}

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

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

【讨论】:

    【解决方案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 类和B 对象中工作时调用B-&gt;Draw()
    3. B-&gt;Draw() 静态调用(即类方法)A::Invoke() 从类 A 我们仍在使用 B 对象。
    4. 静态调用 A::Invoke() 调用 $this-&gt;Draw(); 并且我们目前正在使用 B 对象,$this 指的是 ClassB 的一个实例。
    5. 然后我们开始循环。

    上面的代码发生了什么:

    1. 我们创建B 并开始使用它
    2. 我们在B 类和B 对象中工作时调用B-&gt;Draw()
    3. B-&gt;Draw() 静态调用(即类方法)A::Invoke 从类 A BUT 以及在您的代码中,我们仍在使用 B 对象。
    4. 静态调用A::Invoke()调用self::Draw(),这与ClassA::Draw()基本相同,因为它是一个静态方法,我们不关心我们当前正在使用什么对象,我们称之为A的@ 987654349@方法。
    5. A::Draw() 方法按照我们的需要执行。

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

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

    此时我们完全忘记了 B 甚至存在并且只与 A 一起工作

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

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

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

    【讨论】:

    • parent:: 不是静态调用。我假设需要非静态调用 draw 方法,所以这可能行不通。
    • 从表面上看,是的,但如果您有实际的实例数据,则不是。根据您的建议尝试使用任何$this-&gt;someProperty...
    • 当然不行。请记住,您也可以始终拥有静态属性,这可能适合问题作者的需求。
    • 我使用self 而不是$this 测试了您的答案,它确实调用了父母自己的Draw() 方法,这似乎解决了我的具体问题。但我仍然不明白 PHP 是如何处理它的——既然 Draw() 不是一个静态函数,为什么静态 self 完全可以工作?
    • @caiosm1005 看上面的解释,希望对你说清楚
    【解决方案3】:

    正如 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
    
    */
    

    【讨论】:

    • 特性很好,但我的目标是兼容 PHP 5.3(不幸的是)
    【解决方案4】:

    我非常喜欢你的问题,所以我进行了一些研究,并尝试按照我的第一个答案做同样的事情,但没有静态调用:

    <?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
    

    【讨论】:

    • 新实例化的对象将没有当前实例的任何数据,这可能意味着它不会按预期工作。
    • 在构造函数或方法调用中发送实例,无论如何。这回答了问题并解决了循环问题,我不知道作者到底想要达到什么目的。如果您有更好的解决方案,可以随时发布。
    • 这可能需要 OP 极大地重新设计他的类以通过构造函数接受所有必要的数据。就我而言,重新设计是唯一可行的方法。他试图做的事情根本行不通。
    • 我明白,这就是为什么我说,如果你有更好的解决方案 - 发布它,我真的很想为此找到更好的解决方案!
    • 在这种情况下 $me 是什么?我不知道这样的变量,也许你的意思是 $this?
    猜你喜欢
    • 1970-01-01
    • 2011-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2015-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多