MB: In welchem Bereich sind Traits nützlich?

moin,

bei mir z.B. gibt es problemchen die man gut mit einer zusätzlichen Klasse lösen könnte. Einige Problemchen greifen auch auf andere Strukturen zu, sodass man die Struktur ungern abstrakt werden lässt um eine Kopplung zu vermeiden. Wenn mans denn dann gelöst hat, ist dieses Problemchen keines mehr, jedoch hat man einen Salat von Klassen - so ist mein empfinden.

Wie können da Traits weiter helfen, da sie vor allem auch nicht IntelliSens sind.

  • Bei welchen kleinen Problemstellung ist es sinnvoll Traits zu verwenden und nicht mit einer Statischen Klasse (z.B. Validierung eines Formulars) zu agieren (bei einem kleinen puren PHP Framework <= 1MB).
  • Gibt es eine Art PHP-Konvention die beschreibt wie ein Trait zu vormulieren ist? (z.B. Wie viele Abhängikeiten anderer Klassen kann es höchstens geben oder: "DARF ES keine abhängigkeiten geben"?)
  • Wann lohnt es sich von einem Trait zur Klasse zu wechseln???

lgmb

  1. Meine ganze Factory besteht aus Traits, die übrigens auch Unit-Tests recht einfach machen weil sie von Instanzen beliebiger Klassen (Mock) aufgerufen werden können. Hauptsächlich jedoch sind Traits die Lösung in Sachen Wiederverwendbarkeit von CODE.

    Traits

    Ein Trait ist ein Begriff aus der objektorientierten Programmierung und beschreibt eine wiederverwendbare Sammlung von Methoden und Attributen, ähnlich einer Klasse. Desweiteren können diese Methoden von Instanzen beliebiger Klassen benutzt werden. Ein Trait selbst ist nicht instanziierbar.

    Diese Definition sagt eigentlich alles. Und so einfach wie sie sich anhört ist sie auch umzusetzen.

    1. moin,

      schön und klar erklärt aber das weis ich. Meine Frage war aber ne andere. Sorry wenn ich nicht besser ausdrücken kann. Ich hoffer ein Beispiel hilft:

      class BarObject { public $quz; public $tok } trait BazTrait { public function doSomethingWithObject() : void { /* any code */; } } class FooModel { use BazTrait; private $_barObj; public function __construct( BarObject $bar ) { $this->_barObj = $bar; } } $f = new FooModel( new BarObject ); $f->doAnything(

      BarObject soll irgend n Objekt sein und das Trait BazTrait soll Methoden für Objekte in der Art von BarObject bereit stellen. Die Frage ist nun: kann man getrost andere Instanzen & Klassen die es schon in FooModel gibt, im Trait BazTrait verwenden? Ich mein, dann ist die Gefahr der Kopplung da. Ein weiteres Beispiel an dem Codebeispiel:

      trait BazTrait { public function doSomethingWithObject() : void { $this->_fuzObj /* any code with FuzObject */; } } class FooModel { use BazTrait; private $_barObj; private $_fuzObj; public function __construct( BarObject $bar, FuzObject $fuz ) { $this->_barObj = $bar; $this->_fuzObj = $fuz; } }

      Beim Trait BazTrait habe ich, in der Methode doAnythingWithObject(), den Code spezifiziert mit $this->_fuzObj. Im ersten Beispiel ist es gleich wer das Trait BazTrait verwendet, da es nicht gekoppelt und spezifisch ist, nur ein Objekt eben das das Trait behandelt. Im Zweiten Beispiel ist das nicht der Fall, da entsteht eine kopplung mit der Klasse FooModel. Und Dahin gehend meine Fragen-Liste in der Eingangsfrage:

      • gibt es Konventionen
      • Muss man Traits von anderen Klassen & Instanzen abschiermen, sodass sie Autonom sind, oder nicht abschiermen
      • wann ist eine Trait Verwendung Sinnvoll

      Ich hoffe es ist verständlicher geworden.

      lgmb

      1. Hallo MB,

        ich habe schon mehrfach zu Antworten angesetzt, aber wirklich raten kann ich Dir nicht. Traits sind in meinem Wissensspektrum ein spezielles PHP Konstrukt, das ich sonst nirgends kenne. Was PL in seinem Perl-Blog als Trait bezeichnet, ist eine reduzierte Form der PHP Traits, eher sowas wie ein include einer Datei mit Klassenfragmenten, die man nach Bedarf hinzuladen kann.

        Rein technisch ist es möglich, einen Trait zu schreiben der kreuz und quer auf die Methoden und Eigenschaften des Objektes zugreift, das ihn einbindet. Genauso kann das Objekt kreuz und quer auf den Trait zugreifen. Und warum auch nicht? Sobald Du den Trait mit use einbindest, gehst Du davon aus, dass Klasse und Trait nur zusammen glücklich werden können. Einzeln wird keiner von beiden mehr was.

        Du kannst Dir aber auch gezielt Zurückhaltung auferlegen, und Traits einsetzen

        • wie eine zusätzliche abstrakte Basisklasse, von der man erben kann (in dem Fall greift nur die use-nde Klasse auf den Trait zu). Eine andere Sicht auf diese Verwendung wäre der Trait als Default-Implementierung eines Interface, das von einer Klasse implementiert werden soll.
        • wie eine Subklasse, die deine Klasse erweitert (nur der Trait greift auf Elemente der use-nden Klasse zu)

        Gerade in Frameworks macht man es gerne mal so, dass Methoden der Basisklassen abstrakte Methoden der Basisklasse aufrufen, die dann von der Subklasse zu überschreiben sind, um die für diese Subklasse spezifische Funktionalität beizusteuern. Das kannst Du bei einem Trait auch machen, auch dort gibt es abstrakte Methoden. Wer diesen Trait einbindet, muss dann die abstrakte Methode implementieren. Diese Form der Koppelung ist dann gewollt und auch nicht schlimm - Traits brauchen immer eine andere Klasse als Wirt, sie sind nicht eigenständig nutzbar und nicht eigenständig testbar (außer mit einem Test-Wirt).

        Du fragst nach Konventionen. Kennst Du die PSR? Das ist das, was einer Sammlung von Konventionen in PHP am nächsten kommt, aber über Traits schreiben sie nur Formalien (in PSR-12), die Dir wenig nützen. Man kann auch nicht wirklich von anderen Sprachen abschauen, weil Traits in dieser Form eine PHP Spezialität sind (abgesehen von dem gleichnamigen Dings in C++, das aber etwas anderes ist).

        Wenn Du feststellst, dass ein Trait eigentlich ein eigenständiges Objekt sein könnte, dann ist es vermutlich am besten, das auch so zu implementieren und auf Objektkomposition zurückzugreifen, statt auf das usen von Traits.

        Wenn Du Dich mit einem Trait unwohl fühlst, dann höre auf deinen Bauch und mach keinen.

        Rolf - der möglicherweise komplett falsch liegt 😀

        -- sumpsi - posui - clusi
        1. In einem jeden FW findest Du sämtliche Entwurfsmuster wieder. Von MVC über Dependency Injection, Decorator Pattern, Trait Pattern, Factory Pattern, Delegation Pattern, Aggregation Pattern und wie sie alle heißen mögen.

          Es kam jedoch noch nie darauf an, ein bestimmtes Entwurfmuster umzusetzen!

        2. Charakteristisch für Trait Methoden ist die folgende Schreibweise:

          my $self = shift; my $stash = shift || $self->{STASH};

          Während das erste Argument, eine Klasseninstanz via Pfeiloperator übergeben wird, ist das zweite Argument optional. So kann man, weil an dieser Stelle das erste Argument bereits vorliegt, eine Eigenschaft von diesem als Default setzen.

          Ideal auch für Unittests wo Methoden über ein Mockobjekt aufgerufen werden!

      2. Die Frage ist nun: kann man getrost andere Instanzen & Klassen die es schon in FooModel gibt, im Trait BazTrait verwenden?

        Genau das ist ja der der Sinn von Traits: Klassenunabhängig zu sein!

        1. moin,

          Die Frage ist nun: kann man getrost andere Instanzen & Klassen die es schon in FooModel gibt, im Trait BazTrait verwenden?

          Genau das ist ja der der Sinn von Traits: Klassenunabhängig zu sein!

          Danke für die Wertung der Traits. Ich werde mich so gut es geht an das Halten was du und @Rolf B gesagt hast.

          lgmb

          1. Und nicht vergessen: Programmieren ist nicht eine Frage der Entwurfsmuster sondern eine Frage der Kreativität!

            1. Hallo pl,

              hier muss ich einhaken. Programmieren ist keine Kunst (mehr), sondern ein Handwerk und sollte sich nach Möglichkeit an die anerkannten Regeln des Handwerks halten.

              Software Engineering ist ebenfalls keine Kunst, sondern eine Ingenieurstätigkeit und sollte sich daher ebenfalls auf anerkannte Verfahren stützen, wo sie existieren und anwendbar sind.

              Erst wenn man mit den anerkannten Regeln und Verfahren (=Entwurfsmuster) nicht weiterkommt, beginnt der Raum für Kreativität.

              Rolf

              -- sumpsi - posui - clusi
              1. hier muss ich einhaken. Programmieren ist keine Kunst (mehr), sondern ein Handwerk

                Programmieren war schon immer ein Handwerk!