13

ClassAという名前の描画 PHP クラスがあり、これは他の多くの描画クラス ( ClassBなど) によって拡張されています。

親クラスのDraw()メソッドを起動するには、継承されたクラスが必要です。ただし、私の特定の状況では、そのようなメソッドを直接呼び出したくありません (例: parent::Draw())。parent::InvokeDraw()親のコンテキスト内から描画メソッドを呼び出す3 番目の関数 (例: ) が必要です。

説明するコードを次に示します。

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には関数がfooありbar、あたかもそれらが宣言の一部であるかのように機能します。

子クラスのメソッドをオーバーライドすると、特定の実装が別の実装に置き換えられます。

class B extends A {

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

}

これは、次のようBに宣言されたかのようです。

class B {

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

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

}

1つの例外を除いて、メソッドの親の実装は、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

deceze が言ったように、クラスを再設計しない限り、問題の解決策はありません。

PHP 5.4 を使用している場合は、Traitsを使用できます。

それぞれ独自のDraw機能を持つ 2 つの特性を作成します。次に、1 つの特性を親で使用し、もう 1 つの特性を子で使用します。

次に、子から親関数にアクセスするために、親関数に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 に答える
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. クラス ANDオブジェクトB->Draw()内で作業中に呼び出します。BB
  3. B->Draw()A::Invoke()クラスAから静的に(つまりクラスメソッドを)呼び出しますが、まだBオブジェクトを使用しています。
  4. 静的な呼び出しA::Invoke()呼び出しと、現在オブジェクトで$this->Draw();作業しているため、のインスタンスを参照します。B$thisClassB
  5. そして、ここでループします。

上記のコードで何が起こるか:

  1. 私たちはそれを作成Bし、それを使い始めます
  2. クラス ANDオブジェクトB->Draw()内で作業中に呼び出します。BB
  3. B->Draw()A::InvokeクラスAから静的に(つまり、クラスメソッドを意味します)呼び出しますが、コードではまだBオブジェクトを使用しています。
  4. 静的呼び出しA::Invoke()self::Draw()基本的に and と同じです。これはClassA::Draw()静的メソッドであるため、現在作業しているオブジェクトを気にせず、ADraw()メソッドを呼び出します。
  5. A::Draw()メソッドは必要に応じて実行されます。

静的呼び出しを使用しない 2 番目の回答のコードについて、同じ説明を提供します。

  1. 私たちはそれを作成Bし、それを使い始めます
  2. クラス ANDオブジェクトB->Draw()内で作業中に呼び出します。BB
  3. B->Draw() のインスタンスを作成しAます。
  4. B->Draw()これは、以前とA->Invoke()は異なり、クラスのインスタンスであるオブジェクトの操作を開始することを意味します。AB

この時点でB、存在することさえ完全に忘れて、A

  1. A->Invoke()これは、クラス のインスタンスを既に処理しているため、 を呼び出し$this->Draw()ていることを意味します。A->Draw()A
  2. A->Draw()期待どおりに実行されます。

使いやすさの観点からは、いくつかの静的プロパティを定義でき、A::Draw()実行時にそれらを操作できるため、静的メソッドの方が優れていることがわかります。非静的メソッドを使用する場合、メソッドの引数内で必要なデータを渡す必要があります。

これで明確になることを願っています。上記のシーケンスは用語が適切ではありませんが、意図的に書いたもので、その方が流れが理解しやすいと思います。

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