Warum kann PHP Trait keine Schnittstellen implementieren?

Lesezeit: 9 Minuten

Benutzer-Avatar
Leto

Ich frage mich, warum PHP Trait (PHP 5.4) keine Schnittstellen implementieren kann.

Update von der Antwort von user1460043 => …kann keine Klasse erfordern, die sie verwendet, um eine bestimmte Schnittstelle zu implementieren

Ich verstehe, dass es offensichtlich sein könnte, weil die Leute denken könnten, dass, wenn a Class A verwendet a Trait T die eine implementiert interface Ials das Class A sollte die implementieren interface I indirekt (und das ist nicht wahr, weil Class A könnte Trait-Methoden umbenennen).

In meinem Fall ruft mein Merkmal Methoden von der Schnittstelle auf, die die Klasse, die das Merkmal verwendet, implementiert.

Das Merkmal ist tatsächlich eine Implementierung einiger Methoden der Schnittstelle. Also möchte ich im Code “entwerfen”, dass jede Klasse, die mein Merkmal verwenden möchte, die Schnittstelle implementieren muss. Das würde es dem Merkmal ermöglichen, von der Schnittstelle definierte Klassenmethoden zu verwenden und sicherzustellen, dass sie in der Klasse vorhanden sind.

  • Siehe stackoverflow.com/questions/9205083/php-traits-vs-interfaces

    – Mihai8

    2. Februar 2013 um 20:11 Uhr

  • Das ist nicht der Punkt, ich kenne den Unterschied zwischen Traits und Interfaces.

    – Leto

    2. Februar 2013 um 22:16 Uhr

  • Vielleicht gibt es einen technischen Grund, aber ich frage mich, warum Sie das wollen? Sie können ein Merkmal nicht instanziieren, sodass es Ihnen keine Typehinting-Vorteile bringt, wenn es eine Schnittstelle implementiert. Wenn Sie, wie Sie sagen, Klassen zwingen möchten, die das Merkmal verwenden, um eine Schnittstelle zu implementieren, fragen Sie sich, ob eine (abstrakte) Basisklasse besser geeignet wäre.

    Benutzer1914530

    3. Februar 2013 um 5:49 Uhr

  • Sie haben Recht, ich könnte überall abstrakte Klassen verwenden, aber ich aktualisiere meinen Code auf Trait, und es vermeidet Probleme, die ich mit einfacher Vererbung hatte, deshalb verwende ich Trait. Vielleicht ist es in diesem Fall möglich, aber in einigen anderen nicht.

    – Leto

    3. Februar 2013 um 9:46 Uhr

  • Oder einfacher ausgedrückt: Warum gibt es in PHP keine Traits-Typen?

    – nnevala

    13. Februar 2013 um 23:52 Uhr

Benutzer-Avatar
Danack

Die wirklich kurze Version ist einfacher, weil Sie es nicht können. So funktionieren Traits nicht.

Wenn du schreibst use SomeTrait; In PHP sagen Sie dem Compiler (effektiv), dass er den Code aus dem Trait kopieren und in die Klasse einfügen soll, in der er verwendet wird.

Weil die use SomeTrait; innerhalb der Klasse ist, kann es nicht hinzugefügt werden implements SomeInterface zur Klasse, denn das muss außerhalb der Klasse sein.

“Warum gibt es keine Traits-Typen in PHP?”

Weil sie nicht instanziiert werden können. Traits sind wirklich nur ein Sprachkonstrukt (das dem Compiler mitteilt, den Trait-Code zu kopieren und in diese Klasse einzufügen), im Gegensatz zu einem Objekt oder Typ, auf das von Ihrem Code verwiesen werden kann.

Also möchte ich im Code “entwerfen”, dass jede Klasse, die mein Merkmal verwenden möchte, die Schnittstelle implementieren muss.

Dies kann mit einer abstrakten Klasse erzwungen werden use die Eigenschaft und dann die Erweiterung von Klassen daraus.

interface SomeInterface{
    public function someInterfaceFunction();
}

trait SomeTrait {
    function sayHello(){
        echo "Hello my secret is ".static::$secret;
    }
}

abstract class AbstractClass implements SomeInterface{
    use SomeTrait;
}

class TestClass extends AbstractClass {
    static public  $secret = 12345;

    //function someInterfaceFunction(){
        //Trying to instantiate this class without this function uncommented will throw an error
        //Fatal error: Class TestClass contains 1 abstract method and must therefore be 
        //declared abstract or implement the remaining methods (SomeInterface::doSomething)
    //}
}

$test = new TestClass();

$test->sayHello();

Wenn Sie jedoch erzwingen müssen, dass jede Klasse, die eine Eigenschaft verwendet, eine bestimmte Methode hat, verwenden Sie möglicherweise Eigenschaften, bei denen Sie überhaupt abstrakte Klassen hätten sein sollen.

Oder dass Sie Ihre Logik falsch herum haben. Sie sollen verlangen, dass Klassen, die Schnittstellen implementieren, bestimmte Funktionen haben, nicht dass sie sich selbst als eine Schnittstelle implementierend deklarieren müssen, wenn sie bestimmte Funktionen haben.

Bearbeiten

Tatsächlich können Sie abstrakte Funktionen innerhalb von Traits definieren, um eine Klasse zu zwingen, die Methode zu implementieren. z.B

trait LoggerTrait {

    public function debug($message, array $context = array()) {
        $this->log('debug', $message, $context);
    }

    abstract public function log($level, $message, array $context = array());
}

Dies erlaubt Ihnen jedoch immer noch nicht, die Schnittstelle in der Eigenschaft zu implementieren, und riecht immer noch nach schlechtem Design, da Schnittstellen viel besser als Eigenschaften sind, um einen Vertrag zu definieren, den eine Klasse erfüllen muss.

  • Wie würden Sie dies dann vorschlagen, ich habe eine Human-Klasse, diese Klasse ist basierend auf Job in Unterklassen abstrahiert, aber viele dieser Jobs teilen Funktionen, die am besten mit gemeinsam genutztem Code implementiert werden (z. B. benötigen sowohl eine Sekretärin als auch ein Programmierer die type Methode). Können Sie sich vorstellen, wie dies ohne Merkmale implementiert werden könnte?

    – scragar

    30. Juli 2014 um 13:03 Uhr

  • @scragar das solltest du drüben fragen programers.stackexchange.com aber die Kurzversion ist, dass ich „Human“ mit mehreren „Jobs“ zusammensetzen würde, um eine „WorkingHuman“-Klasse zu sein.

    – Danack

    30. Juli 2014 um 16:06 Uhr

  • Einer noch. Wenn die Schnittstelle einen Bewusstseinsvertrag definiert und dieser Vertrag für die meisten Implementierungen üblich ist. Aber diese Implementierungen haben ihren eigenen Typ drei. So etwas wie Command mit ContainerAwareInterface. Aber Comand hat seine eigenen spezifischen Einsatzgebiete. Ich muss mich also jedes Mal wiederholen, wenn ich Container Awareness benötige, aber wenn ich Trait verwende, kann ich keinen eigenen Vertrag für eine bestimmte Schnittstelle definieren. Vielleicht sollten Core-Entwickler Go-Type-Schnittstellen (z. B. Structural Typing) in Betracht ziehen?

    – faules Commit

    9. Oktober 2015 um 8:16 Uhr

  • Es ist wirklich seltsam, da meine Kollegen und ich Traits eigentlich immer nur dann verwenden, wenn wir Code teilen wollen, der in mehreren Klassen benötigt wird, die eine Schnittstelle implementieren, aber von verschiedenen Vorfahren stammen. Außerdem gibt es keine vernünftige Erklärung, warum der Compiler den Code innerhalb der Klasse ändern kann, aber nicht die von dieser Klasse implementierten Schnittstellen. … es ist einfach ein “fehlendes” Feature … “weil du es nicht kannst” erklärt es am besten

    – Sommerhimmel

    18. Mai 2016 um 8:39 Uhr


  • Ich glaube, dass PHP-Core-Entwickler ein wenig Scala studieren sollten, wo Traits als vollwertige Typen gelten … Ich finde es traurig, dass PHP sein Typisierungssystem schrittweise verbessern will, aber vorhandene gut funktionierende Implementierungen nicht berücksichtigt

    – Vinzenz Pazeller

    18. Oktober 2016 um 12:06 Uhr

Da ist ein RFC: Merkmale mit Schnittstellen schlägt vor, der Sprache Folgendes hinzuzufügen:

trait SearchItem implements SearchItemInterface
{
    ...
}

Von der Schnittstelle benötigte Methoden können entweder durch die Eigenschaft implementiert oder als abstrakt deklariert werden, wobei in diesem Fall erwartet wird, dass die Klasse, die die Eigenschaft verwendet, diese implementiert.

Diese Funktion wird derzeit von der Sprache nicht unterstützt, wird aber geprüft (aktueller Status des RFC ist: Unter Diskussion).

  • Ich nehme an, wenn dies bestätigt wird, werden die Leute wollen, dass mehr und mehr Features aus normalen Klassen in Traits implementiert werden. Bis es zwischen ihnen keinen Unterschied mehr gibt und wir eine Art Frankenstein-Charakteristik haben, die Bedenken und Verantwortlichkeiten nicht richtig aufteilt. Wie die beste Antwort unterstreicht, sind Merkmale als Copy-Past-Bequemlichkeit zu betrachten; sollte die Klassengrenzen nicht zu sehr überschreiten. Wir möchten, dass eine Klasse eine Schnittstelle implementiert, unabhängig davon, ob die Implementierung aus direktem Code oder aus der Verwendung eines Merkmals stammt. Schnittstellen in Traits zu implementieren, könnte verwirrend und irreführend sein

    – Kamafeder

    28. Juli 2019 um 17:30 Uhr


  • Eine großartige Anwendung von Traits ist die Bereitstellung einer einfach einzufügenden Standardimplementierung einer Schnittstelle. Wenn Sie sicherstellen möchten, dass ein Merkmal eine Schnittstelle erfüllt, wäre es schön, die Hilfe des Compilers zu haben

    – Der mächtige Chris

    1. November 2019 um 16:45 Uhr

  • Dieser Vorschlag nicht Machen Sie es so, dass eine Klasse, die ein Merkmal verwendet, diese Merkmalsschnittstellen automatisch implementiert (das würde nicht funktionieren, da Sie Merkmalsmethoden umbenennen/ersetzen können). Das schiefe Argument, dass das Bestehen dieser aggressiveren zukünftigen RFCs irgendwie passieren wird, sollte nicht stichhaltig sein

    – Der mächtige Chris

    1. November 2019 um 16:51 Uhr

  • Ich denke, es sollten nur Eigenschaften existieren, keine Schnittstellen, keine abstrakten Klassen. Und Merkmale sollten Typen in der Sprache sein.

    – Dávid Biró

    18. November 2020 um 1:18 Uhr

Benutzer-Avatar
Benutzer1460043

[…] im Code zu “entwerfen”, dass jede Klasse, die mein Merkmal verwenden möchte, die Schnittstelle implementieren muss. Das würde es dem Merkmal ermöglichen, von der Schnittstelle definierte Klassenmethoden zu verwenden und sicherzustellen, dass sie in der Klasse vorhanden sind.

Das klingt sehr vernünftig und ich würde nicht sagen, dass an Ihrem Design etwas falsch sein muss. Eigenschaften wurden mit dieser Idee im Hinterkopf vorgeschlagen, siehe den zweiten Punkt hier:

  • Ein Merkmal bietet eine Reihe von Methoden, die Verhalten implementieren.
  • Ein Merkmal erfordert eine Reihe von Methoden, die als Parameter für das bereitgestellte Verhalten dienen.
  • […]

Schärli et al, Traits: Composable Units of Behaviour, ECOOP’2003, LNCS 2743, S. 248–274, Springer Verlag, 2003, Seite 2

Es wäre also vielleicht angemessener zu sagen, dass Sie eine Eigenschaft wollen benötigen eine Schnittstelle, nicht um sie zu “implementieren”.

Ich sehe keinen Grund, warum es unmöglich sein sollte, diese Funktion “Eigenschaft erfordert (ihre zu implementierenden Consumer-Klassen) eine Schnittstelle” in PHP zu haben, aber derzeit scheint sie zu fehlen.

Wie @Danack in seiner Antwort feststellt, können Sie abstrakte Funktionen in der Eigenschaft verwenden, um sie von Klassen zu “erfordern”, die die Eigenschaft verwenden. Leider können Sie dies nicht mit privaten Funktionen tun.

Benutzer-Avatar
NekoOs

Ich stimme der Antwort von @Danack zu, werde sie jedoch ein wenig ergänzen.

Die wirklich kurze Version ist einfacher, weil Sie es nicht können. So funktionieren Traits nicht.

Mir fallen nur wenige Fälle ein, in denen das, was Sie verlangen, notwendig ist und sich eher als Designproblem denn als Sprachfehler erweist. Stellen Sie sich vor, es gäbe eine Schnittstelle wie diese:

interface Weaponize
{
    public function hasAmmunition();
    public function pullTrigger();
    public function fire();
    public function recharge();
}

EIN Merkmal wurde erstellt, die eine der in der definierten Funktionen implementiert Schnittstelle nutzt dabei aber auch andere Funktionen, die von der definiert werden Schnittstelleanfällig für den Fehler, dass, wenn die Klasse, die das Feature verwendet, die Schnittstelle alles scheitert, den abzug zu betätigen

trait Triggerable
{
    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}

class Warrior
{
    use Triggerable;
}

Eine einfache Lösung besteht darin, einfach die Klasse zu erzwingen, die die verwendet Merkmal um diese Funktionen ebenfalls zu implementieren:

trait Triggerable
{
    public abstract function hasAmmunition();
    public abstract function fire();

    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}

Also die Merkmal ist nicht vollständig abhängig von der Schnittstelleaber ein Vorschlag, eine seiner Funktionen zu implementieren, da bei Verwendung der Merkmal Die Klasse fordert die Implementierung der abstrakten Methoden.

Endgültiges Design

interface Weaponize
{
    public function hasAmmunition();
    public function pullTrigger();
    public function fire();
    public function recharge();
}

trait Triggerable
{
    public abstract function hasAmmunition();
    public abstract function fire();

    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}


class Warrior implements Weaponize
{
    use Triggerable;

    public function hasAmmunition()
    {
        // TODO: Implement hasAmmunition() method.
    }

    public function fire()
    {
        // TODO: Implement fire() method.
    }

    public function recharge()
    {
        // TODO: Implement recharge() method.
    }
}

Bitte entschuldigen Sie mein Deutsch

1353090cookie-checkWarum kann PHP Trait keine Schnittstellen implementieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy