web-dev-qa-db-ja.com

なぜPHP Traitはインターフェイスを実装できないのですか?

なぜPHP Trait(PHP 5.4)はインターフェイスを実装できないのか疑問に思っています。

ser1460043の回答からの更新=> ...特定のインターフェイスを実装するためにそれを使用するクラスを必要としない

Class ATrait Tを実装しているよりも、interface Iを実装しているClass Ainterface Iを使用している場合、人々が考えることができるので、それは明らかであると理解します[SOMECODE] _間接的に(これはClass Aがトレイトメソッドの名前を変更する可能性があるため当てはまりません)。

私の場合、私の特性は、その特性を使用するクラスが実装するインターフェースからメソッドを呼び出しています。

特性は、実際にはインターフェースのいくつかのメソッドの実装です。だから、私の特性を使用したいすべてのクラスがインターフェースを実装しなければならないように、コードを「設計」したい。これにより、トレイトはインターフェイスで定義されたクラスメソッドを使用し、それらがクラスに存在することを確認できます。

77
Leto

あなたができないので、本当に短いバージョンは簡単です。それはトレイトの仕組みではありません。

PHP)でuse SomeTrait;を記述すると、コードをTraitからコピーして、それが使用されているクラスに貼り付けるように(効果的に)コンパイラーに指示しています。

use SomeTrait;はクラス内にあるため、implements SomeInterfaceをクラスに追加することはできません。これはクラスの外部にある必要があるためです。

「PHPでTraits型ではないのはなぜですか?」

インスタンス化できないためです。特性は、コードで参照できるオブジェクトまたはタイプとは対照的に、実際には単なる 言語構成 (特性コードをコピーしてこのクラスに貼り付けるようにコンパイラーに指示します)です。

だから、私は私の特性を使用したいすべてのクラスがインターフェースを実装しなければならないコードで「設計」したいです。

それは、抽象クラスを使用してトレイトをuseし、それからクラスを拡張することで実施できます。

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();

ただし、Traitを使用するクラスに特定のメソッドがあることを強制する必要がある場合は、そもそも抽象クラスであるはずの特性を使用している可能性があります。

または、ロジックが間違った方向にあること。インターフェイスを実装するクラスには特定の機能があることを要求するものであり、特定の機能がある場合、インターフェイスを実装すると宣言する必要があるものではありません。

編集

実際、Traits内で抽象関数を定義して、クラスにメソッドを実装させることができます。例えば.

trait LoggerTrait {

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

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

しかし、これはまだインターフェイスをインターフェイスに実装することを許可せず、クラスが満たす必要のあるコントラクトを定義するインターフェイスよりもインターフェイスの方がはるかに優れているため、デザインが悪いようです。

88
Danack

RFC:インターフェイス付きの特性 があり、以下を言語に追加することが提案されています。

trait SearchItem implements SearchItemInterface
{
    ...
}

インターフェイスが必要とするメソッドは、トレイトによって実装されるか、抽象として宣言されます。その場合、トレイトを使用するクラスが実装することが期待されます。

現在、この機能は言語でサポートされていませんが、検討中です(RFCの現在のステータスは次のとおりです:Under Discussion)。

20
Ilija

[...]私の特性を使用したいすべてのクラスがインターフェースを実装しなければならないコードを「設計」します。これにより、トレイトはインターフェイスで定義されたクラスメソッドを使用でき、それらがクラスに存在することを確認できます。

これは非常に理にかなっているように思えますが、デザインに問題があるとは言いません。この考えを念頭に置いて特性が提案されています。ここの2番目のポイントを参照してください。

  • 特性は、振る舞いを実装するメソッドのセットを提供します
  • 特性は、指定された動作のパラメーターとして機能するメソッドのセットを必要とします
  • [...]

Schärliet al、Traits:Composable Units of Behaviour、ECOOP’2003、LNCS 2743、pp。248–274、Springer Verlag、2003、Page 2

したがって、インターフェースを「実装」するのではなく、requireインターフェースに対する特性が必要であると言う方が適切かもしれません。

PHPでこの「特性が(実装する消費者クラス)インターフェイスを必要とする」機能を使用することが不可能である理由はわかりませんが、現在は欠落しているようです。

@ -Danackが answer で述べているように、トレイトで抽象関数を使用して、トレイトを使用するクラスに「要求」することができます。残念ながら、これを プライベート関数 で行うことはできません。

7
user1460043

@Danackの応答には同意しますが、少し補足します。

あなたができないので、本当に短いバージョンは簡単です。それはトレイトの仕組みではありません。

私は、あなたが要求することが必要であり、言語の障害としてよりも設計上の問題としてより明白であるいくつかのケースしか考えられません。次のようなインターフェースがあると想像してください:

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

インターフェイスで定義された機能の1つを実装するトレイトが作成されましたが、プロセスではインターフェイスで定義された他の機能も使用しますこの機能を使用するクラスがinterfaceを実装していない場合、すべてがトリガーのプルに失敗するというエラーに対して

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

class Warrior
{
    use Triggerable;
}

簡単な解決策は、単純にtraitを使用するクラスにこれらの関数を実装することです:

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

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

したがって、traitinterfaceに完全に依存しているわけではありませんが、traitを使用する場合はクラスの機能の1つを実装する提案です抽象メソッドの実装を要求します。

最終設計

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.
    }
}

私の英語を許してください

0
NekoOs