Welches Entwurfsmuster steckt in Traits
pl
- programmiertechnik
Seit PHP 5.4.0 gibt es in PHP eine Methode der Wiederverwendung von Code, die Traits genannt wird.
Leider hat man vergessen, in der weiteren Erklärung zu erklären welchem Entwurfsmuster PHP-Traits entsprechen. weiß das jemand?
MFG
http://scg.unibe.ch/research/traits
LG andaris
Hallo pl,
eher nicht.
Traits an sich sind ein Konzept der OO, d.h. weniger als ein Pattern. Sie können von einzelnen Sprachen unterschiedlich implementiert werden.
PHP mischt den Trait in die Klasse ein, so dass die Trait-Methoden nachher wie native Methoden der Klasse genutzt werden können.
In JS kann man das im nachbilden, indem man Methoden aus einem Trait-Prototypen in ein Objekt oder einen Klassen-Prototypen[1] kopiert. Ich nehme an, dass das in Perl ähnlich geht.
In einer Sprache, wo Traits nicht Teil der Sprache sind und auch das Methodendictionary nicht zur Laufzeit manipulierbar ist, benötigt man Techniken wie Delegation und Injection, um sich zu behelfen. Das ist dann sprachspezifisch. Zum Pattern reichts es aber meiner Meinung nach immer noch nicht.
Rolf
Ja ich weiß, das ist auch nur ein Objekt. ↩︎
PHP mischt den Trait in die Klasse ein, so dass die Trait-Methoden nachher wie native Methoden der Klasse genutzt werden können.
Das heißt, daß man innerhalb dieser Methoden die aufrufende Instanz zur Verfügung hat und damit auf
Leider habe ich gerade hierzu kein Beispiel gefunden.
In einer Sprache, wo Traits nicht Teil der Sprache sind und auch das Methodendictionary nicht zur Laufzeit manipulierbar ist, benötigt man Techniken wie Delegation und Injection, um sich zu behelfen.
Das ist so nicht richtig. Traits als Solche kann man auch mit älteren Perl- und PHP- Versionen wo Traits nicht Bestandteil der Sprache sind bauen.
Was das Entwurfsmuster betrifft. das muss wahrscheinlich erst erfunden werden 😉
MFG
Hallo pl,
Traits als Solche kann man auch mit älteren Perl- und PHP- Versionen wo Traits nicht Bestandteil der Sprache sind bauen.
Ja. Sicher. Sag ich doch. Mit Delegation (um den Aufruf zur Trait-Methode zu delegieren) und Injektion (um dem Trait das nutzende Objekt verfügbar zu machen).
Wenn du das als falsch ansiehst, dann zeig doch bitte mal ein PHP Beispiel für deine Sicht her. Und bitte nicht mit __call. Das wäre eine Form von Delegation.
Was das Entwurfsmuster betrifft. das muss wahrscheinlich erst erfunden werden
Wie gesagt:
Traits an sich sind ein Konzept der OO, d.h. weniger als ein Pattern.
Rolf
Hallo pl,
Traits als Solche kann man auch mit älteren Perl- und PHP- Versionen wo Traits nicht Bestandteil der Sprache sind bauen.
Ja. Sicher. Sag ich doch. Mit Delegation (um den Aufruf zur Trait-Methode zu delegieren) und Injektion (um dem Trait das nutzende Objekt verfügbar zu machen).
So wird es auch im Decorator-Pattern gemacht. Ein Nachteil im Vergleich zu einem statischen Trait ist, dass das Verhalten dynamisch hinzugefügt wird. Und ein Decorator kann auch nur auf öffentliche Methoden der Komponenten-Klasse zugreifen.
Hallo pl,
Traits als Solche kann man auch mit älteren Perl- und PHP- Versionen wo Traits nicht Bestandteil der Sprache sind bauen.
Ja. Sicher. Sag ich doch. Mit Delegation (um den Aufruf zur Trait-Methode zu delegieren) und Injektion (um dem Trait das nutzende Objekt verfügbar zu machen).
So wird es auch im Decorator-Pattern gemacht. Ein Nachteil im Vergleich zu einem statischen Trait ist, dass das Verhalten dynamisch hinzugefügt wird. Und ein Decorator kann auch nur auf öffentliche Methoden der Komponenten-Klasse zugreifen.
Deswegen ist das Decoratorpattern keine Alternative zu Traits, denn man möchte in Methoden auf sämtliche Eigenschaften der Instanz zugreifen können und zwar uneingeschränkt.
MFG
Hallo 1unitedpower,
So wird es auch im Decorator-Pattern gemacht
Ja, sicher. Ich habe aber bewusst darauf verzichtet, den Decorator als mögliches Pattern für eine Trait-Implementierung zu bezeichnen, weil es in beiden möglichen Richtungen logisch nicht passt.
(1) Der Trait dekoriert das Nutzerobjekt: Das ist ein vom Schwanz her aufgezäumtes Pferd. Traits sind wiederverwendbar und sollten vom Nutzerobjekt nicht mehr wissen als unbedingt nötig. Ein Trait, der ein Objekt dekoriert, muss aber die gesamte public-Schicht dieses Objekts durchleiten. Geht gar nicht.
(2) Das Nutzerobjekt dekoriert den Trait: Das widerspricht der Idee, dass ein Dekorator dazu dient, ein Objekt zu erweitern, das an sich eigenständig nutzbar und vollständig ist. Der Trait ist kein solches Objekt. Hinzu kommt, dass ein dekoriertes Objekt nichts davon merken sollte, dass es dekoriert wird. Viele Traits benötigen aber Elemente der nutzenden Klasse (was in PHP durch ein geteiltes $this und durch ”just use it“ implementiert wird und aus meiner Sicht eine architektonische Schlangengrube darstellt).
Rolf
Ja, sicher. Ich habe aber bewusst darauf verzichtet, den Decorator als mögliches Pattern für eine Trait-Implementierung zu bezeichnen, weil es in beiden möglichen Richtungen logisch nicht passt.
Ich habe nur an die erste Richtung gedacht.
(1) Der Trait dekoriert das Nutzerobjekt: Das ist ein vom Schwanz her aufgezäumtes Pferd. Traits sind wiederverwendbar und sollten vom Nutzerobjekt nicht mehr wissen als unbedingt nötig. Ein Trait, der ein Objekt dekoriert, muss aber die gesamte public-Schicht dieses Objekts durchleiten. Geht gar nicht.
Die Public-API durchzuschleifen ist nicht elegant, da gebe ich dir absolut recht. Das ist ja aber auch nicht notwendig. Ich würde auch nicht versuchen mit dem Decorator das Verhalten von Traits zu simulieren. Trotzdem kann ich ich mit beiden Techniken Ähnliches erreichen: Ich erweitere einen Datentypen mit einer gewünschten Funktionalität.
Traits als Solche kann man auch mit älteren Perl- und PHP- Versionen wo Traits nicht Bestandteil der Sprache sind bauen.
Ja. Sicher. Sag ich doch. Mit Delegation
Delegation braucht man dafür nicht.
(um den Aufruf zur Trait-Methode zu delegieren) und Injektion (um dem Trait das nutzende Objekt verfügbar zu machen).
Das nutzende Objekt braucht man nicht im Tait sondern in den Methoden eines Trait. So nutzt man nicht einen Trait schlechthin sondern Methoden eines Trait. Von daher wird die aufrufende Instanz nicht an den Trait übergeben sondern an die Methode beim Aufruf derselben, gewöhnlich mit dem Pfeiloperator $instance->foo()
.
Und seitdem in Perl wie auch in PHP Instanzen Referenzen sind, kann man diese Referenzen natürlich auch anderweitig übergeben. Von einer Injektion ist jedoch nur dann die Rede, wenn eine solche Übergabe in den Konstruktor erfolgt. Hierzu wäre anzumerken, daß Traits keine Instanzen ezeugen und damit ist der Begriff der Injektion hier falsch.
MFG
Hallo pl,
wir sprachen von dem Szenario, wo Traits nicht Teil der Sprache sind. Da funktioniert $instance->foo()
nicht, um die Methode foo des Traits aufzurufen.
Wie würde dein PHP 5.3 Nachbau von Traits aussehen?
Rolf
Wie würde dein PHP 5.3 Nachbau von Traits aussehen?
Wie eine ganz normale Methode die eine Instanz übergeben bekommt.
Und wie gesagt, ich suche das Beispiel dafür, wie in Methoden die in PHP über Traits definiert sind, auf Eigenschaften und Methoden der aufrufenden Instanz zugegriffen wird. Also ich gehe mal davon aus, daß infolge der Anwendung des Pfeiloperators mit $o->method()
eine Übergabe stattfindet, so daß man in der Methode die Instanz in $this
vorfindet. Ist das so?
MFG
Hallo pl,
warum probierst Du es nicht aus
Im einfachen Fall sieht das so aus:
<?php
trait RSS {
function _rss() {
echo "Here is your RSS feed for the $this->pageName forum\n";
}
}
class Forum {
use RSS;
private $pageName;
function __construct($name) {
$this->pageName = $name;
}
function control() {
$this->_rss();
}
}
$f = new Forum("Demo");
$f->control();
Aber ich finde es unschön, dass der Trait an den Daten des Objekts herummatscht. Besser finde ich:
<?php
trait RSS {
function _rss() {
echo "Here is your RSS feed for the {$this->getName()} forum\n";
}
abstract function getName();
}
class Forum {
use RSS;
private $pageName;
function __construct($name) {
$this->pageName = $name;
}
function control() {
$this->_rss();
}
function getName() {
return $this->pageName;
}
}
$f = new Forum("Demo");
$f->control();
Hier wird die nutzende Klasse gezwungen, dem Trait die erforderliche Schnittstelle bereitzustellen.
Unter PHP 5.3 dagegen muss man den Trait entweder als Objekt bereitstellen oder statische Methoden verwenden. Ich würde es vielleicht über eine Servicefactory lösen.
<?php
class RSS {
function render($forum) {
echo "Here is your RSS feed for the {$forum->getName()} forum\n";
}
}
class Forum {
private $pageName;
function __construct($name) {
$this->pageName = $name;
}
function control($serviceFactory) {
$rss = $serviceFactory->getService("RSS"); // so
$rss = $serviceFactory->getRssService(); // oder so
$rss->render($this);
}
function getName() {
return $this->pageName;
}
}
$f = new Forum("Demo");
$f->control($serviceFactory);
Vermutlich würde ich diesen Ansatz auch dann wählen, wenn ich PHP 5.4 oder höher hätte. Grund: Der Code für den RSS Feed wird nur gebraucht wenn der RSS Feed explizt angefordert wird. Ansonsten braucht sich PHP damit nicht zu befassen. Und das Darstellen der Topics in HTML würde ich vermutlich über einen weiteren Service implementieren.
Wobei - im konkreten Fall könnte es genügen, zwei unterschiedliche Templates anzuwenden...
Rolf
OK:
trait RSS {
function _rss() {
print_r($this);
}
}
class FF{
use RSS;
function construct(){
$this->DIR = "/";
}
}
$f = new FF();
$f->_rss();
Warum sehe ich da die Eigenschaft nicht? Per Default ist die public.
MFG
Warum sehe ich da die Eigenschaft nicht? Per Default ist die public.
War Tippfehler __construct() muss es heißen, sorry. MFG
neee, lag am guck, wird immer schlächter 😉
(Brille nützt da nichts, ist nervlich bedingt)
Der Code für den RSS Feed wird nur gebraucht wenn der RSS Feed explizt angefordert wird.
Genau das ist der Sinn einer Factory. In meinem FW sind alle Methoden der Factory als Traits implementiert, in Perl wie in PHP. MFG
Unschön ist, daß ein use Traitname;
klassenweit deklariert werden muss. Das setzt voraus, daß man bereits beim Klassenentwurf angeben muss welche Traits verwendet werden sollen. Abgesehen davon daß der Code des Trait dann auch schon kompiliert wird bevor man ihn verwendet.
Für eine Factory ist sowas ungeeignet. MFG
Hallo pl,
ist halt ein statisches Konzept. Willst Du's zur Runtime haben, mach's wie weiter oben vorgeschlagen mit einer Servicefactory.
Traits sind nicht zum Nachladen von Code vorgesehen.
Rolf
Traits sind nicht zum Nachladen von Code vorgesehen.
Meine Traits schon 😉 Die ich im Übrigen automatisch lade.
MFG
Traits sind nicht zum Nachladen von Code vorgesehen.
Es gibt dennoch genügend Fälle wo auch ohnedessen Traits sinnvoll sind. In meinem Beispiel gänge das auch vorausschauend zu programmieren. Denn es steht bereits beim Klassenentwurf fest, daß die Klasse Forum wie die Klasse RSS eine Methode benutzt die in beiden Fällen identisch sind.
Ein solcher Fall ist im Grunde ein Fall für Vererbung, also derartige Methoden in derjenigen Klasse zu definieren von der beide der o.g. Klassen erben. Hinsichtlich dessen, es dennoch nicht so zu machen, ein paar Überlegungen:
Allein aus diesen beiden Üerlegungen ergeben sich wichtige Konsequenzen für ein Framework von Skalierbarkeit über Performanze bis Kommerz.
Deswegen hab ichs hier auch nochmal aufgeschrieben.
Aber ich finde es unschön, dass der Trait an den Daten des Objekts herummatscht.
Nun, das haben Methoden so an sich daß sie mit Eigenschaften arbeiten. Sonst wären es ja keine Methoden.
Wobei - im konkreten Fall könnte es genügen, zwei unterschiedliche Templates anzuwenden...
Im konkreten Fall werden RSS Feed's erstellt. Das Template ist für alle Feeds gleich:
<?xml version="1.0"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="%selfurl%" rel="self" type="application/rss+xml" />
<title>%title%</title>
<description>%descr%</description>
<link>%link%</link>
<language>de-de</language>
<dc:creator>%pubisher%</dc:creator>
<TMPL_LOOP NAME="items">
<item>
<dc:creator>%author%</dc:creator>
<title>%title%</title>
<dc:date>%date%</dc:date>
<description><![CDATA[%descr%]]></description>
<guid isPermaLink="true">%link%</guid>
<link>%link%</link>
</item>
</TMPL_LOOP>
</channel>
</rss>
Und kann mit jeder TE gerendert werden die HTML::Template Kompatibel ist (C, PHP, Perl).
MFG
Mein Beispiel:
class Forum{
function control(){
# erzeugt den RSS Feed über einen Parameter im Forum selbst
else if( $this->param('rss') ){
$this->_rss();
}
}
}
_rss() ist eine Methode die in einem Trait definiert ist. Damit ist das möglich:
; erzeugt den RSS Feed durch Bindung einer anderen Klasse an beliebigen URL
[/forum.rss]
short=Forum
class=RSS
tabn=forum
Also daß diese Methode von einer anderen Klasse aus genutzt werden kann. Und natürlich muss innerhalb der _rss() Methode auf die Eigenschaften der aufrufenden Instanz zugegriffen werden um das XML-Template für den RSS Feed mit den richtigen Daten rendern zu können.
Aber eigentlich suche ich ja ein diebezügliches Beispiel in PHP.
MFG
Seit PHP 5.4.0 gibt es in PHP eine Methode der Wiederverwendung von Code, die Traits genannt wird.
Und noch eine Frage: In der Doku steht, daß man Methoden eines Trait genauso verwendet wie jede andere Methode. Für mein Verständnis heißt das, daß man in Trait-Methoden $this
zur Verfügung hat. Ist das so?
MFG
Hallo pl,
ja, ist so. Und du hast recht: die Doku erwähnt das nicht ausdrücklich, aber die Kommentare auf dieser Seite gehen darauf ein. Such einfach man nach $this.
Rolf
ja, ist so. Und du hast recht: die Doku erwähnt das nicht ausdrücklich, aber die Kommentare auf dieser Seite gehen darauf ein. Such einfach man nach $this.
Hab ich da wohl übersehen, danke. Also ich denke daß vielen PHP-Programmierern gar nicht klar ist daß mit dem Pfeiloperator eine Übergabe der Instanz stattfindet wenn Methoden aufgerufen werden.
MFG