我有一个名为 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( )方法,从而导致无限循环。
虽然问题相当合理,但我很难找到解决方法。如何完成这项任务?
答案 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
的功能为foo
和bar
,就好像它们是宣言的一部分一样。
子类中的覆盖方法将一个特定的实现替换为另一个:
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
修改强> 要回答下面的评论,我将简要介绍代码的工作原理以及代码的工作原理。
您的代码中会发生什么:
B
并开始使用B->Draw()
班级和B
对象内工作时致电B
。B->Draw()
静态调用(这意味着类方法)来自A类 BUT 的A::Invoke()
我们仍在使用B
个对象。A::Invoke()
调用$this->Draw();
,当我们正在使用B
对象时,$this
指的是ClassB
的实例。上面的代码会发生什么:
B
并开始使用B->Draw()
班级和B
对象内工作时致电B
。B->Draw()
从A类 BUT 以及代码中静态调用(表示类方法)A::Invoke
,我们仍在使用B
个对象。< / LI>
A::Invoke()
调用self::Draw()
,它与ClassA::Draw()
基本相同,因为它是一个静态方法,我们不关心我们当前使用的是什么对象,我们称之为A
的{{1}}方法。Draw()
方法可以根据需要执行。我将在第二个答案中为代码提供相同的解释,它不使用静态调用:
A::Draw()
并开始使用B
班级和B->Draw()
对象内工作时致电B
。B
创建 B->Draw()
的实例。A
调用B->Draw()
,这意味着我们开始使用的对象是类A->Invoke()
的实例,而不是之前的A
。 此时我们完全忘记了B
甚至存在且仅适用于B
A
调用A->Invoke()
,这意味着我们正在调用$this->Draw()
,因为我们已经使用了类A->Draw()
的实例。A
按预期执行。从可用性的角度来看,我们可以看到静态方法更好,因为我们可以定义一些静态属性,并且可以在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