Kann ich eine Klasse mit mehr als einer Klasse in PHP erweitern?


150

Kann ich eine Klasse erweitern, wenn ich mehrere Klassen mit Funktionen habe, die ich benötige, aber für die Organisation separat speichern möchte?

dh class a extends b extends c

Bearbeiten: Ich weiß, wie man Klassen einzeln erweitert, aber ich suche nach einer Methode, um eine Klasse sofort mit mehreren Basisklassen zu erweitern - AFAIK Sie können dies in PHP nicht tun, aber es sollte Möglichkeiten geben, dies zu umgehen, ohne darauf zurückzugreifen class c extends b,class b extends a


Verwenden Sie Aggregation oder Schnittstellen. Mehrfachvererbung existiert in PHP nicht.
Franck

2
Ich beschäftige mich mit Schnittstellen, da ich kein großer Fan großer Klassenhierarchien bin. Aber ich kann nicht sehen, wie Schnittstellen tatsächlich etwas tun?
Atomicharri

Mit Schnittstellen können Sie nur die API "erben", keine Funktionskörper. Es zwingt Klasse a, Methoden von Schnittstelle b und c zu implementieren. Wenn Sie Verhalten erben möchten, müssen Sie Mitgliedsobjekte der Klassen b und c in Ihrer Klasse a aggregieren.
Franck


2
Bitte betrachten Sie Merkmale als die richtige Antwort.
Daniel

Antworten:


169

Beantworten Sie Ihre Bearbeitung:

Wenn Sie wirklich Mehrfachvererbung vortäuschen möchten, können Sie die magische Funktion __call () verwenden.

Dies ist hässlich, obwohl es aus Sicht des Benutzers der Klasse A funktioniert:

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");

Druckt "abcdef"


1
Die Idee, die Klasse zu erweitern, gefällt mir eigentlich sehr. Gibt es bekannte Einschränkungen, wie man das so macht?
Atomicharri

3
Soweit ich weiß, gibt es keine Einschränkungen. PHP ist eine sehr freizügige Sprache für solche kleinen Hacks. :) Wie andere bereits betont haben, ist dies jedoch nicht die richtige OOP-Methode.
Franck

64
Sie können keine geschützten oder privaten Methoden verwenden.
Wurmhit

1
@wormhit, ich würde es jedoch nicht für die Verwendung in der Produktion empfehlen. Mit ReflectionClass kann auf private und geschützte Methoden zugegriffen werden.
Denis V

2
Dies funktioniert nicht für Implements, bei denen erwartet wird, dass die Methode tatsächlich vorhanden ist, und das Verhalten ist ziemlich undefiniert, wenn mehrere Basisklassen eine Methode mit demselben Namen haben. Es wird auch die Fähigkeit Ihres Editors beeinträchtigen, Ihnen Hinweise zu geben.
Erik

138

Sie können keine Klasse haben, die zwei Basisklassen erweitert. Das könntest du nicht haben.

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

Sie könnten jedoch eine Hierarchie wie folgt haben ...

Matron extends Nurse    
Consultant extends Doctor

Nurse extends HumanEntity
Doctor extends HumanEntity

HumanEntity extends DatabaseTable
DatabaseTable extends AbstractTable

und so weiter.


Können Sie erklären, warum es richtig ist, zuerst die Krankenschwester an die Matrone zu erben und dann die Vererbung von HumanEntity an die Krankenschwester zu erklären?
Qwerty

7
@Qwerty Weil Matrone zusätzliche Qualitäten einer Krankenschwester hat, während eine Krankenschwester alle Qualitäten eines Menschen hat. Daher ist Matron eine menschliche Krankenschwester und hat schließlich Matron Fähigkeiten
J-Dizzle

58

Sie könnten Eigenschaften verwenden, die hoffentlich ab PHP 5.4 verfügbar sein werden.

Traits ist ein Mechanismus zur Wiederverwendung von Code in einzelnen Vererbungssprachen wie PHP. Ein Merkmal soll einige Einschränkungen der Einzelvererbung verringern, indem es einem Entwickler ermöglicht wird, Methodensätze in mehreren unabhängigen Klassen, die in unterschiedlichen Klassenhierarchien leben, frei wiederzuverwenden. Die Semantik der Kombination von Merkmalen und Klassen ist so definiert, dass die Komplexität verringert und die typischen Probleme vermieden werden, die mit Mehrfachvererbung und Mixins verbunden sind.

Sie sind für ihr Potenzial zur Unterstützung einer besseren Komposition und Wiederverwendung bekannt, weshalb sie in neuere Versionen von Sprachen wie Perl 6, Squeak, Scala, Slate und Fortress integriert sind. Merkmale wurden auch nach Java und C # portiert.

Weitere Informationen: https://wiki.php.net/rfc/traits


Stellen Sie sicher, dass Sie sie nicht missbrauchen. Im Wesentlichen handelt es sich um statische Funktionen, die Sie auf Objekte anwenden. Sie könnten ein Chaos verursachen, indem Sie sie missbrauchen.
Nikola Petkanski

2
Ich kann nicht glauben, dass dies nicht die ausgewählte Antwort ist.
Daniel

18

Klassen sind nicht nur Sammlungen von Methoden. Eine Klasse soll ein abstraktes Konzept darstellen, mit sowohl Zustand (Felder) als auch Verhalten (Methoden), das den Zustand ändert. Die Verwendung der Vererbung, nur um das gewünschte Verhalten zu erzielen, klingt nach einem schlechten OO-Design und genau der Grund, warum viele Sprachen die Mehrfachvererbung nicht zulassen: Um die "Spaghetti-Vererbung" zu verhindern, dh 3 Klassen zu erweitern, da jede eine Methode hat, die Sie benötigen, und am Ende mit Eine Klasse, die 100 Methoden und 20 Felder erbt, jedoch immer nur 5 davon verwendet.


1
Ich werde Ihre Behauptung in Frage stellen, dass die Anfrage des OP ein "schlechtes OO-Design" darstellt . Schauen Sie sich nur die Entstehung von Mixins an, um die Position zu unterstützen, dass das Hinzufügen von Methoden zu einer Klasse aus mehreren Quellen architektonisch eine gute Idee ist. Ich werde Ihnen jedoch mitteilen, dass PHP keine optimalen Sprachfunktionen bietet, um ein optimales "Design" zu erzielen. Dies bedeutet jedoch nicht, dass die Verwendung der verfügbaren Funktionen zur Annäherung notwendigerweise eine schlechte Idee ist. Schauen Sie sich einfach die Antwort von @ Franck an.
MikeSchinkel

15

Ich glaube, es gibt Pläne, bald Mix-Ins hinzuzufügen.

Aber bis dahin gehen Sie mit der akzeptierten Antwort. Sie können das ein wenig abstrahieren, um eine "erweiterbare" Klasse zu erstellen:

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();

Beachten Sie, dass "OtherClass" in diesem Modell $ foo kennenlernt. OtherClass benötigt eine öffentliche Funktion namens "setExtendee", um diese Beziehung einzurichten. Wenn die Methoden dann von $ foo aus aufgerufen werden, kann intern auf $ foo zugegriffen werden. Es wird jedoch keinen Zugriff auf private / geschützte Methoden / Variablen erhalten, wie dies eine echte erweiterte Klasse tun würde.


12

Verwenden Sie Merkmale als Basisklassen. Verwenden Sie sie dann in einer übergeordneten Klasse. Erweitere es .

trait business{
  function sell(){

  }

  function buy(){

  }

  function collectMoney(){
  }

}

trait human{

   function think(){

   }

   function speak(){

   }

}

class BusinessPerson{
  use business;
  use human;
  // If you have more traits bring more
}


class BusinessWoman extends BusinessPerson{

   function getPregnant(){

   }

}


$bw = new BusinessWoman();
$bw ->speak();
$bw->getPregnant();

Siehe jetzt Geschäftsfrau logisch geerbtes Geschäft und Mensch beide;


Ich bin mit PHP 5.3 festgefahren, es gibt keine Unterstützung für Eigenschaften: D
Nishchal Gautam

Warum nicht das Merkmal BusinessWomananstelle von verwenden BusinessPerson? Dann können Sie eine tatsächliche Mehrfachvererbung haben.
SOFe

@ SOFe, weil BusinessMan es auch erweitern kann. Wer also Geschäfte macht, kann die Geschäftsperson verlängern und erhält automatisch Eigenschaften
Alice

Die Art und Weise, wie Sie dies tun, unterscheidet sich nicht von der Möglichkeit einer einzelnen Vererbung, bei der BusinessPerson eine abstrakte Klasse namens "human" erweitert. Mein Punkt ist, dass Ihr Beispiel nicht wirklich Mehrfachvererbung benötigt.
SOFe

Ich glaube, bis BusinessPerson es erforderlich war, hätte BusinessWoman andere Eigenschaften direkt erben können. Aber was ich hier versucht habe, ist, beide Eigenschaften in einer Mediator-Klasse zu behalten und sie dann zu verwenden, wo immer dies erforderlich ist. Daher kann ich vergessen, beide Merkmale einzuschließen, wenn ich wieder etwas anderes benötige (wie von BusinessMan beschrieben). Es dreht sich alles um Codestile. Wenn Sie direkt in Ihre Klasse aufnehmen möchten. Sie können damit weitermachen - da haben Sie absolut Recht.
Alice

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

4
Ihre Antwort sollte eine Erklärung Ihres Codes und eine Beschreibung enthalten, wie das Problem gelöst wird.
AbcAeffchen

1
Es ist fast selbsterklärend, aber es wäre toll, Kommentare entlang des Codes zu haben.
Heroselohim

Wenn nun Ext1 und Ext2 beide eine Funktion mit demselben Namen haben, wird immer Ext1 aufgerufen. Dies hängt überhaupt nicht von den Parametern ab.
Alice

8

Derzeit Antwort von @Franck akzeptiert wird funktionieren , aber es ist nicht in Mehrfachvererbung Tatsache , sondern ein Kind Instanz der Klasse des Bereichs definiert aus, auch gibt es die __call()Kurzschrift - man denke nur unter Verwendung von $this->childInstance->method(args)überall Sie ExternalClass Klassenmethode in „erweiterten“ Klasse benötigen.

Genaue Antwort

Nein, Sie können nicht, nicht wirklich, wie das Handbuch des extendsSchlüsselworts sagt:

Eine erweiterte Klasse ist immer von einer einzelnen Basisklasse abhängig, dh Mehrfachvererbung wird nicht unterstützt.

Echte Antwort

Wie @adam jedoch richtig vorgeschlagen hat, verbietet dies NICHT die Verwendung mehrerer hierarchischer Vererbungen.

Sie können eine Klasse mit einer anderen und eine andere mit einer anderen erweitern und so weiter ...

Ein ziemlich einfaches Beispiel hierfür wäre:

class firstInheritance{}
class secondInheritance extends firstInheritance{}
class someFinalClass extends secondInheritance{}
//...and so on...

Wichtige Notiz

Wie Sie vielleicht bemerkt haben, können Sie nur dann mehrere (2+) Intehritanzen nach Hierarchie durchführen, wenn Sie die Kontrolle über alle im Prozess enthaltenen Klassen haben. Dies bedeutet, dass Sie diese Lösung nicht anwenden können, z. B. mit integrierten Klassen oder mit Klassen, die Sie verwenden kann einfach nicht bearbeitet werden - wenn Sie das tun möchten, bleibt Ihnen die @Franck-Lösung - untergeordnete Instanzen.

... und zum Schluss Beispiel mit etwas Ausgabe:

class A{
  function a_hi(){
    echo "I am a of A".PHP_EOL."<br>".PHP_EOL;  
  }
}

class B extends A{
  function b_hi(){
    echo "I am b of B".PHP_EOL."<br>".PHP_EOL;  
  }
}

class C extends B{
  function c_hi(){
    echo "I am c of C".PHP_EOL."<br>".PHP_EOL;  
  }
}

$myTestInstance = new C();

$myTestInstance->a_hi();
$myTestInstance->b_hi();
$myTestInstance->c_hi();

Welche Ausgänge

I am a of A 
I am b of B 
I am c of C 

6

Ich habe mehrere Artikel gelesen, die von der Vererbung in Projekten (im Gegensatz zu Bibliotheken / Frameworks) abraten und dazu ermutigen, agaisnt-Schnittstellen zu programmieren, nicht gegen Implementierungen.
Sie befürworten auch OO nach Zusammensetzung: Wenn Sie die Funktionen in Klasse a und b benötigen, machen Sie c mit Mitgliedern / Feldern dieses Typs:

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();
    }
}

Dies wird effektiv das gleiche, was Franck und Sam zeigen. Wenn Sie sich für die explizite Verwendung der Komposition entscheiden, sollten Sie natürlich die Abhängigkeitsinjektion verwenden .
MikeSchinkel

3

Mehrfachvererbung scheint auf Schnittstellenebene zu funktionieren. Ich habe einen Test auf PHP 5.6.1 gemacht.

Hier ist ein Arbeitscode:

<?php


interface Animal
{
    public function sayHello();
}


interface HairyThing
{
    public function plush();
}

interface Dog extends Animal, HairyThing
{
    public function bark();
}


class Puppy implements Dog
{
    public function bark()
    {
        echo "ouaf";
    }

    public function sayHello()
    {
        echo "hello";
    }

    public function plush()
    {
        echo "plush";
    }


}


echo PHP_VERSION; // 5.6.1
$o = new Puppy();
$o->bark();
$o->plush();
$o->sayHello(); // displays: 5.6.16ouafplushhello

Ich dachte nicht, dass das möglich wäre, aber ich bin auf den SwiftMailer-Quellcode in der Swift_Transport_IoBuffer-Klasse gestoßen, der die folgende Definition hat:

interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream

Ich habe noch nicht damit gespielt, aber ich dachte, es könnte interessant sein, es zu teilen.


1
interessant und irrelevant.
Jewgenij Afanasjew

1

Ich habe gerade mein Problem der "Mehrfachvererbung" gelöst mit:

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

}

Auf diese Weise kann ich die Sitzung innerhalb einer SessionResponse bearbeiten, wodurch MyServiceResponsetype erweitert wird und die Sitzung weiterhin selbst verarbeitet werden kann.


1

Wenn Sie überprüfen möchten, ob eine Funktion öffentlich ist, lesen Sie dieses Thema: https://stackoverflow.com/a/4160928/2226755

Verwenden Sie die Methode call_user_func_array (...) für viele oder nicht Argumente.

So was :

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");


0

PHP unterstützt noch nicht die Vererbung mehrerer Klassen, jedoch die Vererbung mehrerer Schnittstellen.

Einige Beispiele finden Sie unter http://www.hudzilla.org/php/6_17_0.php .


Noch? Ich bezweifle, dass sie es tun werden. Moderne OO raten von Mehrfachvererbung ab, es kann chaotisch sein.
PhiLho

0

PHP erlaubt keine Mehrfachvererbung, aber Sie können mehrere Schnittstellen implementieren. Wenn die Implementierung "schwer" ist, stellen Sie eine Skelettimplementierung für jede Schnittstelle in einer separaten Klasse bereit. Anschließend können Sie alle Schnittstellenklassen über die Objekteinschließung an diese Skelettimplementierungen delegieren .


0

Da Sie nicht genau wissen, was Sie erreichen möchten, würde ich empfehlen, die Möglichkeit zu prüfen, Ihre Anwendung so umzugestalten, dass in diesem Fall eher Komposition als Vererbung verwendet wird.


0

Es ist immer eine gute Idee, eine übergeordnete Klasse mit Funktionen zu erstellen ... dh diese gesamte Funktionalität dem übergeordneten Element hinzuzufügen.

Und "verschieben" Sie alle Klassen, die dies hierarchisch verwenden, nach unten. Ich brauche - Funktionen neu schreiben, die spezifisch sind.


0

Eines der Probleme von PHP als Programmiersprache ist die Tatsache, dass Sie nur eine einzige Vererbung haben können. Dies bedeutet, dass eine Klasse nur von einer anderen Klasse erben kann.

In den meisten Fällen wäre es jedoch vorteilhaft, von mehreren Klassen zu erben. Beispielsweise kann es wünschenswert sein, Methoden von mehreren verschiedenen Klassen zu erben, um eine Codeduplizierung zu verhindern.

Dieses Problem kann zu einer Klasse führen, die eine lange Familiengeschichte der Vererbung hat, die oft keinen Sinn ergibt.

In PHP 5.4 wurde eine neue Funktion der Sprache hinzugefügt, die als Eigenschaften bekannt ist. Ein Merkmal ist insofern wie ein Mixin, als es Ihnen ermöglicht, Merkmalsklassen in eine vorhandene Klasse zu mischen. Dies bedeutet, dass Sie die Codeduplizierung reduzieren und die Vorteile nutzen können, während Sie die Probleme der Mehrfachvererbung vermeiden.

Züge


-1

Dies ist keine echte Antwort, wir haben eine doppelte Klasse

...Aber es funktioniert :)

class A {
    //do some things
}

class B {
    //do some things
}

Klasse copy_B ist das Kopieren aller Klassen B.

class copy_B extends A {

    //do some things (copy class B)
}

class A_B extends copy_B{}

jetzt

class C_A extends A{}
class C_B extends B{}
class C_A_b extends A_B{}  //extends A & B

-3

Klasse A erweitert B {}

Klasse B erweitert C {}

Dann hat A sowohl B als auch C erweitert


2
@rostamiani, da diese Methode auch die Klasse ändert B. Was wir die meiste Zeit wollen, ist, zwei nicht verwandte Klassen Bund C(wahrscheinlich aus verschiedenen Bibliotheken) zu haben und gleichzeitig von geerbt zu werden A, so dass Aalles von beiden Bund C, aber Bnichts von Cund umgekehrt enthält.
SOFe
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.