Ich habe in letzter Zeit versucht, mich mit PHP zu beschäftigen, und bin dabei, mich auf Eigenschaften einzulassen. Ich verstehe das Konzept der Wiederverwendung von horizontalem Code und möchte nicht unbedingt von einer abstrakten Klasse erben. Was ich nicht verstehe ist: Was ist der entscheidende Unterschied zwischen der Verwendung von Merkmalen und Schnittstellen?
Ich habe versucht, nach einem anständigen Blog-Beitrag oder Artikel zu suchen, in dem erklärt wird, wann der eine oder andere verwendet werden soll, aber die Beispiele, die ich bisher gefunden habe, scheinen so ähnlich zu sein, dass sie identisch sind.
Imagick
Objekte zu gehen und zu sprechen , abzüglich all des Aufblähens, das in den alten Tagen vor Merkmalen benötigt wurde.Antworten:
Eine Schnittstelle definiert eine Reihe von Methoden, die die implementierende Klasse implementieren muss .
Wenn ein Merkmal vorhanden ist
use
, kommen auch die Implementierungen der Methoden hinzu - was in einem nicht der Fall istInterface
.Das ist der größte Unterschied.
Aus der horizontalen Wiederverwendung für PHP RFC :
quelle
Mitteilung des öffentlichen Dienstes:
Ich möchte festhalten, dass ich glaube, dass Merkmale fast immer ein Codegeruch sind und zugunsten der Komposition vermieden werden sollten. Ich bin der Meinung, dass eine einzelne Vererbung häufig bis zu einem Anti-Muster missbraucht wird und eine mehrfache Vererbung dieses Problem nur noch verstärkt. In den meisten Fällen werden Sie viel besser bedient, wenn Sie die Komposition der Vererbung vorziehen (sei es einzeln oder mehrfach). Wenn Sie immer noch an Merkmalen und deren Beziehung zu Schnittstellen interessiert sind, lesen Sie weiter ...
Beginnen wir damit:
Um OO-Code zu schreiben, müssen Sie verstehen, dass es bei OOP wirklich um die Funktionen Ihrer Objekte geht. Sie müssen über Klassen nachdenken, was sie können, anstatt was sie tatsächlich tun . Dies steht in krassem Gegensatz zur traditionellen prozeduralen Programmierung, bei der der Schwerpunkt darauf liegt, ein bisschen Code "etwas tun" zu lassen.
Wenn es beim OOP-Code um Planung und Design geht, ist eine Schnittstelle die Blaupause und ein Objekt das vollständig konstruierte Haus. In der Zwischenzeit sind Merkmale lediglich eine Möglichkeit, das durch die Blaupause (die Schnittstelle) festgelegte Haus zu bauen.
Schnittstellen
Warum sollten wir also Schnittstellen verwenden? Schnittstellen machen unseren Code ganz einfach weniger spröde. Wenn Sie an dieser Aussage zweifeln, fragen Sie jeden, der gezwungen war, Legacy-Code zu pflegen, der nicht für Schnittstellen geschrieben wurde.
Die Schnittstelle ist ein Vertrag zwischen dem Programmierer und seinem Code. Die Benutzeroberfläche sagt: "Solange Sie sich an meine Regeln halten, können Sie mich implementieren, wie Sie möchten, und ich verspreche, dass ich Ihren anderen Code nicht brechen werde."
Stellen Sie sich als Beispiel ein reales Szenario vor (keine Autos oder Widgets):
Sie schreiben zunächst eine Klasse, um Anforderungsantworten mit APC zwischenzuspeichern:
Anschließend suchen Sie in Ihrem HTTP-Antwortobjekt nach einem Cache-Treffer, bevor Sie die gesamte Arbeit ausführen, um die tatsächliche Antwort zu generieren:
Dieser Ansatz funktioniert hervorragend. Aber vielleicht ein paar Wochen später entscheiden Sie sich, ein dateibasiertes Cache-System anstelle von APC zu verwenden. Jetzt müssen Sie Ihren Controller-Code ändern, da Sie Ihren Controller so programmiert haben, dass er mit der Funktionalität der
ApcCacher
Klasse und nicht mit einer Schnittstelle arbeitet, die die Funktionen derApcCacher
Klasse ausdrückt . Nehmen wir an, Sie hätten dieController
KlasseCacherInterface
stattdessen auf a anstatt auf Beton gesetztApcCacher
:Dazu definieren Sie Ihre Benutzeroberfläche folgendermaßen:
Im Gegenzug müssen sowohl Ihre
ApcCacher
als auch Ihre neuenFileCacher
Klassen die implementierenCacherInterface
und IhreController
Klasse so programmieren , dass sie die für die Schnittstelle erforderlichen Funktionen nutzt.Dieses Beispiel zeigt (hoffentlich), wie Sie durch Programmieren auf eine Schnittstelle die interne Implementierung Ihrer Klassen ändern können, ohne sich Sorgen machen zu müssen, ob die Änderungen Ihren anderen Code beschädigen.
Züge
Merkmale hingegen sind lediglich eine Methode zur Wiederverwendung von Code. Schnittstellen sollten nicht als sich gegenseitig ausschließende Alternative zu Merkmalen betrachtet werden. In der Tat ist das Erstellen von Merkmalen, die die für eine Schnittstelle erforderlichen Funktionen erfüllen, der ideale Anwendungsfall .
Sie sollten Merkmale nur verwenden, wenn mehrere Klassen dieselbe Funktionalität verwenden (wahrscheinlich von derselben Schnittstelle vorgegeben). Es macht keinen Sinn, ein Merkmal zu verwenden, um Funktionalität für eine einzelne Klasse bereitzustellen: Dies verschleiert nur, was die Klasse tut, und ein besseres Design würde die Funktionalität des Merkmals in die relevante Klasse verschieben.
Betrachten Sie die folgende Implementierung von Merkmalen:
Ein konkreteres Beispiel: Stellen Sie sich vor, Sie
FileCacher
und IhreApcCacher
Mitarbeiter aus der Schnittstellendiskussion verwenden dieselbe Methode, um festzustellen, ob ein Cache-Eintrag veraltet ist und gelöscht werden sollte (dies ist im wirklichen Leben offensichtlich nicht der Fall, aber machen Sie mit). Sie können ein Merkmal schreiben und beiden Klassen erlauben, es für die allgemeine Schnittstellenanforderung zu verwenden.Ein letztes Wort zur Vorsicht: Achten Sie darauf, dass Sie nicht mit Merkmalen über Bord gehen. Oft werden Merkmale als Krücke für schlechtes Design verwendet, wenn eindeutige Klassenimplementierungen ausreichen würden. Sie sollten Merkmale darauf beschränken, die Schnittstellenanforderungen für das beste Code-Design zu erfüllen.
quelle
A
trait
ist im Wesentlichen die PHP-Implementierung von amixin
und ist effektiv eine Reihe von Erweiterungsmethoden, die durch Hinzufügen von zu jeder Klasse hinzugefügt werden könnentrait
. Die Methoden werden dann Teil der Implementierung dieser Klasse, jedoch ohne Vererbung .Aus dem PHP-Handbuch (Schwerpunkt Mine):
Ein Beispiel:
Mit dem oben definierten Merkmal kann ich jetzt Folgendes tun:
Wenn ich zu diesem Zeitpunkt eine Klasseninstanz erstelle
MyClass
, gibt es zwei Methoden,foo()
und undbar()
-, die von kommenmyTrait
. Und - beachten Sie, dass dietrait
-defined-Methoden bereits einen Methodenkörper haben - was eineInterface
-defined-Methode nicht kann.Darüber hinaus verwendet PHP wie viele andere Sprachen ein einzelnes Vererbungsmodell. Dies bedeutet, dass eine Klasse von mehreren Schnittstellen abgeleitet werden kann, jedoch nicht von mehreren Klassen. Eine PHP-Klasse kann jedoch mehrere
trait
Einschlüsse haben, wodurch der Programmierer wiederverwendbare Teile einschließen kann, wie dies bei mehreren Basisklassen der Fall sein könnte.Ein paar Dinge zu beachten:
Polymorphismus:
Im vorherigen Beispiel, wo
MyClass
erweitertSomeBaseClass
,MyClass
ist eine Instanz vonSomeBaseClass
. Mit anderen Worten, ein Array wieSomeBaseClass[] bases
kann Instanzen von enthaltenMyClass
. Und fallsMyClass
erweitertIBaseInterface
, ein Array ausIBaseInterface[] bases
könnte Instanzen enthaltenMyClass
. Mit a ist kein solches polymorphes Konstrukt verfügbar,trait
da atrait
im Wesentlichen nur Code ist, der zur Vereinfachung für den Programmierer in jede Klasse kopiert wird, die ihn verwendet.Vorrang:
Wie im Handbuch beschrieben:
Stellen Sie sich also das folgende Szenario vor:
Beim Erstellen einer Instanz von MyClass (siehe oben) tritt Folgendes auf:
Interface
IBase
erfordertSomeMethod()
die Bereitstellung einer parameterlosen Funktion .BaseClass
bietet eine Implementierung dieser Methode, um die Anforderungen zu erfüllen.trait
myTrait
bietet auch eine parameterlose FunktionSomeMethod()
, die Vorrang vor derBaseClass
-version hatclass
MyClass
bietet eine eigene Version vonSomeMethod()
-, die Vorrang vor dertrait
-version hat.Fazit
Interface
kann keine Standardimplementierung eines Methodenkörpers bereitstellen, während einetrait
Dose.Interface
ist ein polymorphes , geerbtes Konstrukt - atrait
nicht.Interface
s kann in der gleichen Klasse verwendet werden, und so kann mehreretrait
s.quelle
mixin
- und als ich die Eröffnung meiner Antwort noch einmal durchgesehen habe, habe ich aktualisiert, um dies widerzuspiegeln. Danke für den Kommentar, @BoltClock!Ich denke,
traits
es ist nützlich, Klassen zu erstellen, die Methoden enthalten, die als Methoden mehrerer verschiedener Klassen verwendet werden können.Zum Beispiel:
Sie können diese "Fehler" -Methode in jeder Klasse verwenden, die dieses Merkmal verwendet.
Während mit können
interfaces
Sie nur die Methodensignatur deklarieren, nicht aber den Funktionscode. Um eine Schnittstelle zu verwenden, müssen Sie außerdem einer Hierarchie folgenimplements
. Dies ist bei Merkmalen nicht der Fall.Es ist ganz anders!
quelle
to_integer
wäre eher in einerIntegerCast
Schnittstelle enthalten, da es keinen grundsätzlich ähnlichen Weg gibt, Klassen (intelligent) in eine Ganzzahl umzuwandeln.use Toolkit
Ihnen könnte$this->toolkit = new Toolkit();
oder vermisse ich einen Vorteil des Merkmals selbst?Something
Container, den Sie tunif(!$something->do_something('foo')) var_dump($something->errors);
Für Anfänger kann die obige Antwort schwierig sein. Dies ist der einfachste Weg, sie zu verstehen:
Züge
Wenn Sie also
sayHello
Funktionen in anderen Klassen haben möchten, ohne die gesamte Funktion neu zu erstellen, können Sie Merkmale verwenden.Cool richtig!
Nicht nur Funktionen, die Sie im Merkmal verwenden können (Funktion, Variablen, Konst.). Sie können auch mehrere Merkmale verwenden:
use SayWorld,AnotherTraits;
Schnittstelle
So unterscheidet sich die Schnittstelle von den Merkmalen: Sie müssen alles in der Schnittstelle in der implementierten Klasse neu erstellen. Schnittstelle hat keine Implementierung. und die Schnittstelle kann nur Funktionen und const haben, sie kann keine Variablen haben.
Ich hoffe das hilft!
quelle
Dies ist in den meisten Fällen eine gute Art, darüber nachzudenken, aber es gibt eine Reihe subtiler Unterschiede zwischen den beiden.
Zunächst einmal arbeitet der
instanceof
Operator nicht mit Merkmalen (dh ein Merkmal ist kein reales Objekt), sodass Sie uns nicht mitteilen können, ob eine Klasse ein bestimmtes Merkmal aufweist (oder ob zwei ansonsten nicht verwandte Klassen ein Merkmal gemeinsam haben) ). Das ist es, was sie damit meinen, dass es ein Konstrukt für die Wiederverwendung von horizontalem Code ist.In PHP gibt es jetzt Funktionen, mit denen Sie eine Liste aller von einer Klasse verwendeten Merkmale abrufen können. Die Vererbung von Merkmalen bedeutet jedoch, dass Sie rekursive Überprüfungen durchführen müssen, um zuverlässig zu überprüfen, ob eine Klasse irgendwann ein bestimmtes Merkmal aufweist (es gibt ein Beispiel) Code auf den PHP-Doku-Seiten). Aber ja, es ist sicherlich nicht so einfach und sauber wie eine Instanz davon, und meiner Meinung nach ist es eine Funktion, die PHP besser machen würde.
Außerdem sind abstrakte Klassen immer noch Klassen, sodass sie keine Probleme bei der Wiederverwendung von Code mit Mehrfachvererbung lösen. Denken Sie daran, dass Sie nur eine Klasse (real oder abstrakt) erweitern, aber mehrere Schnittstellen implementieren können.
Ich habe festgestellt, dass Merkmale und Schnittstellen sehr gut Hand in Hand verwendet werden können, um eine Pseudo-Mehrfachvererbung zu erstellen. Z.B:
Auf diese Weise können Sie mithilfe von instanceof feststellen, ob das jeweilige Door-Objekt mit einem Schlüssel versehen ist oder nicht. Sie wissen, dass Sie einen konsistenten Satz von Methoden usw. erhalten und dass sich der gesamte Code in allen Klassen, die das KeyedTrait verwenden, an einer Stelle befindet.
quelle
Merkmale dienen lediglich der Wiederverwendung von Code .
Die Schnittstelle liefert lediglich die Signatur der Funktionen, die in der Klasse definiert werden sollen, in der sie je nach Ermessen des Programmierers verwendet werden kann . So erhalten wir einen Prototyp für eine Gruppe von Klassen .
Als Referenz http://www.php.net/manual/en/language.oop5.traits.php
quelle
Sie können ein Merkmal grundsätzlich als automatisiertes "Kopieren und Einfügen" von Code betrachten.
Die Verwendung von Merkmalen ist gefährlich, da es keine Möglichkeit gibt, vor der Ausführung zu wissen, was sie tun.
Merkmale sind jedoch flexibler, da sie keine Einschränkungen wie Vererbung aufweisen.
Merkmale können nützlich sein, um eine Methode einzufügen, die etwas in eine Klasse eincheckt, z. B. das Vorhandensein einer anderen Methode oder eines anderen Attributs. Ein schöner Artikel dazu (aber auf Französisch, sorry) .
Für französisch lesende Menschen, die es bekommen können, hat das GNU / Linux-Magazin HS 54 einen Artikel zu diesem Thema.
quelle
Wenn Sie Englisch sprechen und wissen, was es
trait
bedeutet, ist es genau das, was der Name sagt. Es handelt sich um ein klassenloses Paket von Methoden und Eigenschaften, die Sie durch Eingabe an vorhandene Klassen anhängenuse
.Grundsätzlich können Sie es mit einer einzelnen Variablen vergleichen. Closures-Funktionen können
use
diese Variablen von außerhalb des Gültigkeitsbereichs verwenden und haben auf diese Weise den Wert innerhalb. Sie sind mächtig und können in allem eingesetzt werden. Gleiches passiert mit Merkmalen, wenn sie verwendet werden.quelle
Andere Antworten haben die Unterschiede zwischen Schnittstellen und Merkmalen hervorragend erklärt. Ich werde mich auf ein nützliches Beispiel aus der realen Welt konzentrieren, insbesondere eines, das zeigt, dass Merkmale Instanzvariablen verwenden können, sodass Sie einer Klasse mit minimalem Code für das Boilerplate Verhalten hinzufügen können.
Wie bereits von anderen erwähnt, lassen sich Merkmale gut mit Schnittstellen kombinieren, sodass die Schnittstelle den Verhaltensvertrag und das Merkmal zur Erfüllung der Implementierung angeben kann.
Das Hinzufügen von Funktionen zum Veröffentlichen / Abonnieren von Ereignissen zu einer Klasse kann in einigen Codebasen ein häufiges Szenario sein. Es gibt 3 gängige Lösungen:
use
das Merkmal, auch bekannt als Import, verwenden, um die Funktionen zu erhalten.Wie gut funktioniert jeder?
# 1 Funktioniert nicht gut. Bis zu dem Tag, an dem Sie feststellen, dass Sie die Basisklasse nicht erweitern können, weil Sie bereits etwas anderes erweitern. Ich werde kein Beispiel dafür zeigen, da es offensichtlich sein sollte, wie einschränkend es ist, eine solche Vererbung zu verwenden.
# 2 & # 3 funktionieren beide gut. Ich werde ein Beispiel zeigen, das einige Unterschiede hervorhebt.
Zunächst ein Code, der zwischen beiden Beispielen gleich ist:
Eine Schnittstelle
Und etwas Code, um die Verwendung zu demonstrieren:
Ok, jetzt zeigen wir, wie sich die Implementierung der
Auction
Klasse bei der Verwendung von Merkmalen unterscheidet.Hier ist zunächst, wie # 2 (mit Komposition) aussehen würde:
So würde # 3 (Merkmale) aussehen:
Beachten Sie, dass der Code in der
EventEmitterTrait
Datei genau mit dem in derEventEmitter
Klasse übereinstimmt, außer dass das Merkmal dietriggerEvent()
Methode als geschützt deklariert . Also, der einzige Unterschied , den Sie brauchen , zu betrachten ist die Implementierung derAuction
Klasse .Und der Unterschied ist groß. Wenn wir Komposition verwenden, erhalten wir eine großartige Lösung, die es uns ermöglicht, unsere
EventEmitter
von so vielen Klassen wiederzuverwenden , wie wir möchten. Der Hauptnachteil ist jedoch, dass wir viel Boilerplate-Code haben, den wir schreiben und pflegen müssen, da wir ihn für jede in derObservable
Schnittstelle definierte Methode implementieren und langweiligen Boilerplate-Code schreiben müssen, der die Argumente nur an die entsprechende Methode in weiterleitet Wir haben dasEventEmitter
Objekt komponiert . Durch die Verwendung des Merkmals in diesem Beispiel können wir dies vermeiden und den Code für die Boilerplate reduzieren und die Wartbarkeit verbessern .Es kann jedoch vorkommen, dass Ihre
Auction
Klasse nicht die vollständigeObservable
Schnittstelle implementieren soll. Vielleicht möchten Sie nur 1 oder 2 Methoden oder gar keine verfügbar machen, damit Sie Ihre eigenen Methodensignaturen definieren können. In einem solchen Fall bevorzugen Sie möglicherweise immer noch die Kompositionsmethode.Das Merkmal ist jedoch in den meisten Szenarien sehr überzeugend, insbesondere wenn die Schnittstelle viele Methoden enthält, was dazu führt, dass Sie viele Boilerplates schreiben.
* Sie könnten tatsächlich beides tun - definieren Sie die
EventEmitter
Klasse, falls Sie sie jemals kompositorisch verwenden möchten, und definieren Sie dasEventEmitterTrait
Merkmal auch mithilfe derEventEmitter
Klassenimplementierung innerhalb des Merkmals :)quelle
Das Merkmal ist dasselbe wie eine Klasse, die wir für mehrere Vererbungszwecke und auch für die Wiederverwendbarkeit von Code verwenden können.
Wir können Merkmale innerhalb der Klasse verwenden und wir können auch mehrere Merkmale in derselben Klasse mit 'Schlüsselwort verwenden' verwenden.
Die Schnittstelle wird für die Wiederverwendbarkeit von Code wie ein Merkmal verwendet
Die Schnittstelle besteht aus mehreren Schnittstellen, damit wir die Probleme mit der Mehrfachvererbung lösen können. Wenn wir jedoch die Schnittstelle implementieren, sollten wir alle Methoden innerhalb der Klasse erstellen. Für weitere Informationen klicken Sie auf den folgenden Link:
http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php
quelle
Eine Schnittstelle ist ein Vertrag, der besagt, dass „dieses Objekt in der Lage ist, dieses Ding zu machen“, während ein Merkmal dem Objekt die Fähigkeit gibt, das Ding zu machen.
Ein Merkmal ist im Wesentlichen eine Möglichkeit, Code zwischen Klassen zu kopieren und einzufügen.
Versuchen Sie, diesen Artikel zu lesen. Was sind PHP-Merkmale?
quelle
Der Hauptunterschied besteht darin, dass Sie bei Schnittstellen die tatsächliche Implementierung jeder Methode in jeder Klasse definieren müssen, die diese Schnittstelle implementiert, sodass viele Klassen dieselbe Schnittstelle implementieren können, jedoch mit unterschiedlichem Verhalten, während Merkmale nur eingebaute Codestücke sind eine Klasse; Ein weiterer wichtiger Unterschied besteht darin, dass Merkmalsmethoden nur Klassenmethoden oder statische Methoden sein können, im Gegensatz zu Schnittstellenmethoden, die auch Instanzmethoden sein können (und normalerweise sind).
quelle