169

必要であるが、整理のために別々に保存したい関数を持つ複数のクラスがある場合、両方を持つようにクラスを拡張できますか?

すなわちclass a extends b extends c

編集:一度に1つずつクラスを拡張する方法は知っていますが、複数の基本クラスを使用してクラスを即座に拡張する方法を探しています-私の知る限り、PHPではこれを行うことはできませんが、に頼らずに回避する方法があるはずですclass c extends bclass b extends a

4

20 に答える 20

181

PHP 5.3で多重継承を偽造したい場合は、魔法の関数__call()を使用できます。

これは、クラスAユーザーの観点からは機能しますが、醜いです。

class B {
    public function method_from_b($s) {
        echo $s;
    }
}

class C {
    public function method_from_c($s) {
        echo $s;
    }
}

class A extends B
{
  private $c;
    
  public function __construct()
  {
    $this->c = new C;
  }
    
  // fake "extends C" using magic function
  public function __call($method, $args)
  {
    $this->c->$method($args[0]);
  }
}


$a = new A;
$a->method_from_b("abc");
$a->method_from_c("def");

「abcdef」を出力します

于 2008-12-10T15:18:14.360 に答える
144

2つの基本クラスを拡張するクラスを持つことはできません。あなたは次のものを持つことができませんでした:

// this is NOT allowed (for all you google speeders)
Matron extends Nurse, HumanEntity

ただし、次のような階層を持つことができます...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

等々。

于 2008-12-10T14:12:37.783 に答える
66

特性を使用できます。これは、PHP5.4から利用できるようになることを願っています。

特性は、PHPなどの単一継承言語でコードを再利用するためのメカニズムです。トレイトは、開発者が異なるクラス階層に存在するいくつかの独立したクラスでメソッドのセットを自由に再利用できるようにすることで、単一継承のいくつかの制限を減らすことを目的としています。トレイトとクラスの組み合わせのセマンティクスは、複雑さを軽減し、多重継承とミックスインに関連する一般的な問題を回避する方法で定義されます。

それらは、より良い構成と再利用をサポートする可能性があることで認識されているため、Perl 6、Squeak、Scala、Slate、Fortressなどの新しいバージョンの言語に統合されています。トレイトはJavaとC#にも移植されています。

詳細情報:https ://wiki.php.net/rfc/traits

于 2011-06-24T10:50:44.420 に答える
18

間もなくミックスインを追加する計画があると思います。

しかし、それまでは、受け入れられた答えに従ってください。それを少し抽象化して、「拡張可能な」クラスを作成できます。

class Extendable{
  private $extender=array();

  public function addExtender(Extender $obj){
    $this->extenders[] = $obj;
    $obj->setExtendee($this);
  }

  public function __call($name, $params){
    foreach($this->extenders as $extender){
       //do reflection to see if extender has this method with this argument count
       if (method_exists($extender, $name)){
          return call_user_func_array(array($extender, $name), $params);
       }
    }
  }
}


$foo = new Extendable();
$foo->addExtender(new OtherClass());
$foo->other_class_method();

このモデルでは、「OtherClass」が$fooについて「知っている」ことに注意してください。OtherClassには、この関係を設定するために「setExtendee」と呼ばれるパブリック関数が必要です。次に、メソッドが$ fooから呼び出された場合、内部で$fooにアクセスできます。ただし、実際の拡張クラスのように、プライベート/保護されたメソッド/変数にアクセスすることはできません。

于 2008-12-11T06:22:08.637 に答える
18

クラスは単なるメソッドの集まりではありません。クラスは、状態 (フィールド) と状態を変更する動作 (メソッド) の両方を持つ抽象的な概念を表すと想定されています。望ましい動作を得るために継承を使用することは、OO 設計が悪いように聞こえます。また、多くの言語が多重継承を禁止するまさにその理由は、「スパゲッティ継承」を防ぐためです。つまり、それぞれに必要なメソッドがあるため 3 つのクラスを拡張し、最終的には100 個のメソッドと 20 個のフィールドを継承するクラスですが、そのうち 5 個しか使用しません。

于 2008-12-10T14:48:10.143 に答える
10
<?php
// what if we want to extend more than one class?

abstract class ExtensionBridge
{
    // array containing all the extended classes
    private $_exts = array();
    public $_this;

    function __construct() {$_this = $this;}

    public function addExt($object)
    {
        $this->_exts[]=$object;
    }

    public function __get($varname)
    {
        foreach($this->_exts as $ext)
        {
            if(property_exists($ext,$varname))
            return $ext->$varname;
        }
    }

    public function __call($method,$args)
    {
        foreach($this->_exts as $ext)
        {
            if(method_exists($ext,$method))
            return call_user_method_array($method,$ext,$args);
        }
        throw new Exception("This Method {$method} doesn't exists");
    }


}

class Ext1
{
    private $name="";
    private $id="";
    public function setID($id){$this->id = $id;}
    public function setName($name){$this->name = $name;}
    public function getID(){return $this->id;}
    public function getName(){return $this->name;}
}

class Ext2
{
    private $address="";
    private $country="";
    public function setAddress($address){$this->address = $address;}
    public function setCountry($country){$this->country = $country;}
    public function getAddress(){return $this->address;}
    public function getCountry(){return $this->country;}
}

class Extender extends ExtensionBridge
{
    function __construct()
    {
        parent::addExt(new Ext1());
        parent::addExt(new Ext2());
    }

    public function __toString()
    {
        return $this->getName().', from: '.$this->getCountry();
    }
}

$o = new Extender();
$o->setName("Mahdi");
$o->setCountry("Al-Ahwaz");
echo $o;
?>
于 2014-10-07T00:17:59.640 に答える
6

私はいくつかの記事を読んだことがあり、プロジェクトでの継承を (ライブラリ/フレームワークとは対照的に) 思いとどまらせ、実装に対してではなくインターフェイスに対してプログラミングすることを奨励しています。
彼らはまた、合成による OO を提唱しています。クラス a と b の関数が必要な場合は、この型のメンバー/フィールドを持つ c を作成します。

class C
{
    private $a, $b;

    public function __construct($x, $y)
    {
        $this->a = new A(42, $x);
        $this->b = new B($y);
    }

    protected function DoSomething()
    {
        $this->a->Act();
        $this->b->Do();
    }
}
于 2008-12-10T17:02:06.847 に答える
1

PHP 5.4 で発表された PHP の Traits を使用してそれを行うことができます。

ここに簡単なチュートリアルがありますhttp://culttt.com/2014/06/25/php-traits/

于 2016-03-24T13:56:15.840 に答える
1

「多重継承」の問題を次の方法で解決しました。

class Session {
    public $username;
}

class MyServiceResponsetype {
    protected $only_avaliable_in_response;
}

class SessionResponse extends MyServiceResponsetype {
    /** has shared $only_avaliable_in_response */

    public $session;

    public function __construct(Session $session) {
      $this->session = $session;
    }

}

このようにして、MyServiceResponsetype を拡張した SessionResponse 内でセッションを操作する力があり、Session を単独で処理できます。

于 2012-02-01T10:24:45.180 に答える
1

関数が公開されているかどうかを確認したい場合は、このトピックを参照してください: https://stackoverflow.com/a/4160928/2226755

また、多くの引数またはそうでない引数には call_user_func_array(...) メソッドを使用します。

このような :

class B {
    public function method_from_b($s) {
        echo $s;
    }
}

class C {
    public function method_from_c($l, $l1, $l2) {
        echo $l.$l1.$l2;
    }
}

class A extends B {
    private $c;

    public function __construct() {
        $this->c = new C;
    }

    public function __call($method, $args) {
        if (method_exists($this->c, $method)) {
            $reflection = new ReflectionMethod($this->c, $method);
            if (!$reflection->isPublic()) {
                throw new RuntimeException("Call to not public method ".get_class($this)."::$method()");
            }

            return call_user_func_array(array($this->c, $method), $args);
        } else {
            throw new RuntimeException("Call to undefined method ".get_class($this)."::$method()");
        }
    }
}


$a = new A;
$a->method_from_b("abc");
$a->method_from_c("d", "e", "f");
于 2013-12-23T10:07:19.687 に答える
1

プログラミング言語としての PHP の問題の 1 つは、継承が 1 つしかないことです。これは、クラスが他の 1 つのクラスからのみ継承できることを意味します。

ただし、多くの場合、複数のクラスから継承すると便利です。たとえば、コードの重複を防ぐために、いくつかの異なるクラスからメソッドを継承することが望ましい場合があります。

この問題は、多くの場合意味をなさない継承の長い家族歴を持つクラスにつながる可能性があります。

PHP 5.4 では、Traits と呼ばれる言語の新しい機能が追加されました。Trait は、Trait クラスを既存のクラスに混在させることができるという点で、一種の Mixin に似ています。これは、多重継承の問題を回避しながら、コードの重複を減らしてメリットを得ることができることを意味します。

特徴

于 2017-05-06T11:13:28.347 に答える
0

PHPは多重継承を許可していませんが、複数のインターフェースを実装することで可能です。実装が「重い」場合は、個別のクラスの各インターフェースに骨格実装を提供します。次に、オブジェクトの包含を介して、すべてのインターフェイスクラスをこれらのスケルトン実装に委任できます。

于 2008-12-10T15:21:00.890 に答える
0

PHPはまだ複数のクラスの継承をサポートしていませんが、複数のインターフェースの継承をサポートしています。

いくつかの例については、 http://www.hudzilla.org/php/6_17_0.phpを参照してください。

于 2008-12-10T14:07:28.027 に答える
0

何を達成しようとしているのか正確にはわからないので、この場合、継承ではなく構成を使用するようにアプリケーションを再設計する可能性を検討することをお勧めします。

于 2009-01-25T05:26:45.977 に答える
0

常に良い考えは、関数を使用して親クラスを作成することです...つまり、このすべての機能を親に追加します。

そして、これを使用するすべてのクラスを階層的に「移動」します。私は必要です - 特定の関数を書き直してください。

于 2011-05-01T11:02:47.173 に答える