2008-09-18 5 views
90

Ich bin auf der Suche nach einem guten, sauberen Weg, um die Tatsache zu umgehen, dass PHP5 immer noch keine Mehrfachvererbung unterstützt. Hier ist die Klassenhierarchie:Mehrere Vererbung in PHP

Nachricht
- Textmessage
-------- InvitationTextMessage
- Emailmessage
-------- InvitationEmailMessage

Die beiden Arten von Einladung * Klassen haben viel gemeinsam; Ich würde gerne eine gemeinsame Elternklasse, Einladung, haben, von der beide erben würden. Leider haben sie auch viel mit ihren aktuellen Vorfahren gemeinsam ... TextMessage und EmailMessage. Klassischer Wunsch nach Mehrfachvererbung hier.

Was ist der leichteste Ansatz, um das Problem zu lösen?

Danke!

+4

Es gibt nicht viele Fälle, in denen Vererbung (oder sogar Mehrfachvererbung) gerechtfertigt ist. Sehen Sie sich die SOLID-Prinzipien an. Zusammensetzung vor Vererbung bevorzugen. –

+2

@ OndřejMirtes was meinst du - "nicht viele Fälle, in denen Vererbung gerechtfertigt ist."? – styler1972

+9

Ich meine - Vererbung bringt mehr Probleme als Vorteile (siehe Liskov Substitutionsprinzip). Sie können fast alles mit der Zusammensetzung lösen und viele Kopfschmerzen sparen. Vererbung ist auch statisch - das bedeutet, dass Sie nicht ändern können, was bereits im Code geschrieben ist. Aber Compositition kann zur Laufzeit verwendet werden, und Sie können Implementierungen dynamisch auswählen - e. G. die gleiche Klasse mit unterschiedlichen Caching-Mechanismen wiederverwenden. –

Antwort

139

Alex, die meisten Male benötigen Sie mehrere Vererbung ist ein Signal Ihre Objektstruktur ist etwas falsch. In der Situation, die Sie skizziert haben, sehe ich, dass die Klassenverantwortung einfach zu weit gefasst ist. Wenn die Nachricht Teil des Anwendungsgeschäftsmodells ist, sollte die Ausgabe nicht berücksichtigt werden. Stattdessen können Sie die Verantwortung teilen und MessageDispatcher verwenden, der die Nachricht sendet, die mit dem Text- oder HTML-Backend übergeben wurde. Ich weiß nicht, Ihren Code, aber lassen Sie es mich so simulieren:

$m = new Message(); 
$m->type = 'text/html'; 
$m->from = 'John Doe <[email protected]>'; 
$m->to = 'Random Hacker <[email protected]>'; 
$m->subject = 'Invitation email'; 
$m->importBody('invitation.html'); 

$d = new MessageDispatcher(); 
$d->dispatch($m); 

Auf diese Weise können einige Spezialisierung auf Message-Klasse hinzufügen:

$htmlIM = new InvitationHTMLMessage(); // html type, subject and body configuration in constructor 
$textIM = new InvitationTextMessage(); // text type, subject and body configuration in constructor 

$d = new MessageDispatcher(); 
$d->dispatch($htmlIM); 
$d->dispatch($textIM); 

Beachten Sie, dass MessageDispatcher eine Entscheidung treffen würde, ob Als HTML oder als Nur-Text senden, abhängig von type Eigenschaft in Message-Objekt übergeben.

// in MessageDispatcher class 
public function dispatch(Message $m) { 
    if ($m->type == 'text/plain') { 
     $this->sendAsText($m); 
    } elseif ($m->type == 'text/html') { 
     $this->sendAsHTML($m); 
    } else { 
     throw new Exception("MIME type {$m->type} not supported"); 
    } 
} 

Um es zusammenzufassen, ist die Verantwortung zwischen zwei Klassen aufgeteilt. Die Nachrichtenkonfiguration erfolgt in der InvitationHTMLMessage/InvitationTextMessage-Klasse, und der Sendealgorithmus wird an den Dispatcher delegiert.Dies nennt man Strategy Pattern, mehr dazu lesen Sie unter here.

+12

Erstaunlich umfangreiche Antwort, danke! Ich habe heute etwas gelernt! –

+22

...Ich weiß, dass das ein bisschen alt ist (ich suchte, ob PHP MI hatte ... nur aus Neugier) Ich denke nicht, dass dies ein gutes Beispiel für das Strategie-Muster ist. Das Strategy Pattern ist so angelegt, dass Sie jederzeit eine neue "Strategie" implementieren können. Die Implementierung, die Sie bereitgestellt haben, verfügt nicht über diese Fähigkeit. Stattdessen sollte Message die "send" -Funktion haben, die MessageDispatcher-> dispatch() (Dispatcher entweder param oder member var) aufruft, und neue Klassen HTMLDispatcher & TextDispatcher implementieren "dispatch" auf ihre jeweilige Weise (dies erlaubt anderen Dispatcher) um andere Arbeiten zu machen) –

+5

Ich wünschte wirklich, dass SO sich nicht mit meinem Whitespace vermasseln würde ... –

6

Das Symfony-Framework hat eine mixin plugin for this, die Sie vielleicht ausprobieren sollten - auch nur für Ideen, wenn nicht zu verwenden.

Die Antwort "Entwurfsmuster" besteht darin, die freigegebene Funktionalität in eine separate Komponente zu abstrahieren und zur Laufzeit zu erstellen. Stellen Sie sich eine Möglichkeit vor, die Einladungsfunktionalität als eine Klasse herauszufiltern, die mit Ihren Nachrichtenklassen in einer anderen Weise als der Vererbung verknüpft ist.

+0

der Symfony-Link ist gebrochen ... – m13r

0

Das gleiche Problem wie Java. Versuchen Sie die Verwendung von Schnittstellen mit abstrakten Funktionen zur Lösung dieses Problems

13

Vielleicht können Sie eine 'ist-a' Relation mit einer 'hat-a' Beziehung ersetzen? Eine Einladung hat möglicherweise eine Nachricht, aber sie muss nicht unbedingt eine Nachricht "ist-a" haben. Eine Einladung f. könnte bestätigt werden, was nicht gut mit dem Nachrichtenmodell zusammenpasst.

Suchen Sie nach 'Komposition vs. Vererbung', wenn Sie mehr darüber wissen möchten.

0

PHP unterstützt Schnittstellen. Dies könnte eine gute Wette sein, abhängig von Ihren Anwendungsfällen.

+4

Schnittstellen nicht erlauben Sie konkrete Funktionsimplementierungen, also sind sie hier nicht hilfreich. –

+0

Schnittstellen unterstützen Multiple Vererbung im Gegensatz zu Klassen. –

1

Ich habe ein paar Fragen zu klären, zu fragen, was Sie tun:

1) Gibt es in Ihrem Nachrichtenobjekt nur eine Nachricht enthalten z.B. Körper, Empfänger, Zeitplan? 2) Was beabsichtigen Sie mit Ihrem Einladungsobjekt zu tun? Muss es speziell im Vergleich zu einer EmailMessage behandelt werden? 3) Wenn ja, WAS ist das Besondere daran? 4) Wenn das der Fall ist, warum müssen die Nachrichtentypen für eine Einladung anders gehandhabt werden? 5) Was ist, wenn Sie eine Willkommensnachricht oder eine OK-Nachricht senden möchten? Sind sie auch neue Objekte?

Es hört sich so an, als würden Sie versuchen, zu viele Funktionen zu einer Menge von Objekten zu kombinieren, die sich nur mit dem Inhalt einer Nachricht beschäftigen sollten - und nicht, wie sie behandelt werden sollte. Für mich gibt es keinen Unterschied zwischen einer Einladung oder einer Standardnachricht. Wenn die Einladung eine spezielle Behandlung erfordert, dann bedeutet das Anwendungslogik und kein Nachrichtentyp.

Zum Beispiel: ein System, das ich gebaut hatte, hatte eine gemeinsame Basis-Nachricht Objekt, das in SMS, E-Mail und andere Nachrichtentypen erweitert wurde. Allerdings: Diese wurden nicht weiter erweitert - eine Einladungsnachricht war einfach vordefinierter Text, der über eine Nachricht vom Typ Email versendet wurde. Ein spezifischer Einladungsantrag würde sich mit der Validierung und anderen Anforderungen für eine Einladung befassen. Alles, was Sie tun wollen, ist, die Nachricht X an den Empfänger Y zu senden, der ein eigenständiges System sein sollte.

3

Dies ist sowohl eine Frage und eine Lösung ....

Was ist mit dem magischen _ call(), _get(), __set() Methoden? Ich habe diese Lösung noch nicht getestet, aber was ist, wenn Sie eine multiInherit-Klasse erstellen. Eine geschützte Variable in einer untergeordneten Klasse könnte ein Array von zu erben Klassen enthalten. Der Konstruktor in der Multi-Interface-Klasse könnte Instanzen jeder der vererbten Klassen erstellen und sie mit einer privaten Eigenschaft verknüpfen, sagen wir _ext. Die Methode __call() könnte die Funktion method_exists() für jede der Klassen im Array _ext verwenden, um die richtige Aufrufmethode zu finden. __get() und __set können verwendet werden, um interne Eigenschaften zu suchen, oder wenn Sie ein Experte mit Referenzen sind, könnten Sie die Eigenschaften der untergeordneten Klasse und der geerbten Klassen als Referenzen auf dieselben Daten definieren. Die Mehrfachvererbung Ihres Objekts wäre transparent für den Code, der diese Objekte verwendet. Außerdem können interne Objekte bei Bedarf direkt auf die geerbten Objekte zugreifen, solange das _ext-Array nach Klassennamen indiziert ist. Ich habe mir vorgestellt, diese Superklasse zu erschaffen und habe sie noch nicht implementiert, da ich der Meinung bin, dass es, wenn es funktioniert, dazu führen könnte, dass sich einige sehr schlechte Programmiergewohnheiten entwickeln.

+0

Ich denke, das ist machbar. Es wird die Funktionalität mehrerer Klassen kombinieren, aber sie nicht erben (im Sinne von 'instanceof'). – user102008

+0

Und dies wird sicher keine Überschreibungen erlauben, sobald in der inneren Klasse ein Aufruf an self :: erfolgt –

7

Wenn ich Phil zitieren kann in this thread ...

PHP, wie Java, bietet keine Unterstützung für Mehrfachvererbung.

Kommen in PHP 5.4 wird traits sein, die versuchen, eine Lösung für dieses Problem bereitzustellen.

In der Zwischenzeit sollten Sie Ihr Klassen-Design am besten überdenken. Sie können mehrere Schnittstellen implementieren, wenn Sie nach einer erweiterten API zu Ihre Klassen sind.

Und Chris ....

PHP nicht wirklich Mehrfachvererbung unterstützen, aber es gibt einige (etwas chaotisch) Möglichkeiten, es zu implementieren. Sehen Sie sich diese URL für einige Beispiele:

http://www.jasny.net/articles/how-i-php-multiple-inheritance/

Thought sie beide nützliche Links hatte. Kann nicht warten, um Eigenschaften oder vielleicht etwas mixins auszuprobieren ...

-1

Wie wäre es mit einer Einladungsklasse direkt unterhalb der Nachrichtenklasse?

so die Hierarchie geht:

Nachricht
--- Einladung
------ Textmessage
------ Emailmessage

Und Einladung Klasse, fügen Sie die Funktionalität das war in InvitationTextMessage und in InvitationEmailMessage.

Ich weiß, dass Einladung ist nicht wirklich eine Art von Nachricht, es ist mehr eine Funktionalität von Message. Ich bin mir also nicht sicher, ob das ein gutes OO-Design ist oder nicht.

3

Ich verwende Merkmale in PHP 5.4 als die Möglichkeit, dies zu lösen. http://php.net/manual/en/language.oop5.traits.php

Dies ermöglicht die klassische Vererbung mit erweitert, sondern bietet auch die Möglichkeit, gemeinsame Funktionalität und Eigenschaften in einem "Merkmal" zu platzieren. Wie das Handbuch sagt:

Traits ist ein Mechanismus für die Wiederverwendung von Code in einzelnen Vererbungssprachen wie PHP. Eine Eigenschaft soll einige Einschränkungen der Einzelvererbung reduzieren, indem es einem Entwickler ermöglicht, Methodensätze frei in mehreren unabhängigen Klassen zu verwenden, die in verschiedenen Klassenhierarchien leben.