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


82

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

user1460043の回答からの更新=> ...特定のインターフェースを実装するためにそれを使用するクラスを要求することはできません

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

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

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



13
それは重要ではありません、私は特性とインターフェースの違いを知っています。
レト2013

1
技術的な理由があるかもしれませんが、なぜそうしたいのでしょうか。トレイトをインスタンス化することはできないため、インターフェースを実装しても、タイプヒントのメリットはありません。あなたが言うように、これが特性を使用するクラスにインターフェースを実装するように強制することを望むなら、あなたは(抽象的な)基本クラスがより適切であるかどうか疑問に思います。

確かに、どこでも抽象クラスを使用できますが、コードをTraitに更新しています。これにより、単純な継承で発生した問題が回避されるため、traitを使用しています。したがって、その場合は可能かもしれませんが、そうでない場合もあります。
レト2013

2
あるいは、もっと簡単に言えば、PHPで特性タイプがないのはなぜですか?
nnevala 2013

回答:


97

あなたができないので、本当に短いバージョンはより簡単です。それは特性がどのように機能するかではありません。

use SomeTrait;PHPで書くときは、(事実上)コンパイラーにコードをコピーしてTraitから使用されているクラスに貼り付けるように指示しています。

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

「PHPで特性タイプがないのはなぜですか?」

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

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

これは、抽象クラスを使用し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();

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

または、ロジックが間違っていること。インターフェイスを実装するクラスに特定の関数があることを要求することを意図しています。特定の関数がある場合は、インターフェイスを実装していると宣言する必要があります。

編集

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

trait LoggerTrait {

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

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

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


2
これをどのようにレイアウトすることをお勧めしますか?私はHumanクラスを持っています、このクラスはJobに基づいてサブクラスに抽象化されていますが、これらのジョブの多くは共有コードで最適に実装される機能を共有しています(たとえば、秘書とプログラマーの両方がtypeメソッドを必要とします)。これを特性なしでどのように実装できるか考えられますか?
scragar 2014

あなたがでそれ以上を尋ねるべき@scragar programmers.stackexchange.comが、ショートバージョンは、私は複数の「ジョブ」との複合「人間は」「WorkingHuman」クラスであるだろうとあります。
ダナック2014

1
もう1つ。インターフェイスが何らかの認識コントラクトを定義し、そのコントラクトがほとんどの実装に共通である場合。しかし、これらの実装には独自のタイプ3があります。ContainerAwareInterfaceを使用したコマンドのようなもの。しかし、Comandには独自の特定の使用領域があります。したがって、Container Awarenessが必要になるたびに繰り返す必要がありますが、Traitを使用する場合、特定のインターフェイスに対して独自のコントラクトを定義することはできません。コア開発者は、Go-Typeインターフェイス(構造型入力など)を検討する必要がありますか?
lazycommit 2015年

3
実際、私の同僚と私は、インターフェイスを実装しているが異なる祖先からのいくつかのクラスで必要なコードを共有したい場合にのみ特性を使用するので、それは本当に奇妙です。また、コンパイラがクラス内のコードを変更できるが、このクラスによって実装されているインターフェイスは変更できない理由についての合理的な説明はありません。...それは単に「欠けている」機能です...「できないので」これを最もよく説明しています
Summer-Sky

5
PHPのコア開発者は、特性が本格的な型と見なされるScalaを少し研究する必要があると思います... PHPが徐々に型付けシステムを改善したいと思っているが、既存のうまく機能する実装を考慮していないのは悲しいことです
Vincent Pazeller

28

RFCがあります:インターフェースを持つ特性は、言語に次のものを追加することを示唆しています:

trait SearchItem implements SearchItemInterface
{
    ...
}

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

この機能は現在この言語ではサポートされていませんが、検討中です(RFCの現在のステータスは「議論中」です)。


確認されれば、人々は通常のクラスの機能をトレイトに実装することをますます望んでいると思います。それらの間に違いがなく、懸念と責任を適切に分割しないある種のフランケンシュタインの特性が得られるまで。最良の答えが強調するように、特性はコピー過去の利便性と見なされるべきです。クラスの境界を越えすぎないようにしてください。実装が直接コードからのものであろうと、トレイトの使用からのものであろうと、クラスにインターフェースを実装してもらいたい。特性へのインタフェースは混乱と誤解を招くかもしれない実装を可能にする
Kamafeather

トレイトの優れたアプリケーションの1つは、インターフェイスの貼り付けが簡単なデフォルトの実装を提供することです。特性がインターフェースを満たしていることを確認したい場合は、コンパイラーの助けを借りるとよいでしょう
The

この提案で、トレイトを使用するクラスがそれらのトレイトインターフェイスを自動的に実装するようにはなりません(トレイトメソッドの名前を変更/置換できるため、機能しません)。これを通過することはどういうわけかより積極的な将来のRFCを通過するだろうという滑りやすい坂の議論は水を保持するべきではありません
マイティクリス

10

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

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

  • トレイト、動作を実装する一連のメソッドを提供します
  • トレイトに、提供された動作のパラメーターとして機能する一連のメソッドが必要です。
  • [...]

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

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

この「トレイトには(実装するためのコンシューマークラス)インターフェイスが必要」という機能をPHPで使用できない理由はわかりませんが、現在は欠落しているようです。

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


1

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

あなたができないので、本当に短いバージョンはより簡単です。それは特性がどのように機能するかではありません。

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

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

インターフェイスで定義された関数の1つを実装するトレイトが作成されましたが、プロセスでは、インターフェイスで定義された他の関数を使用します。この機能を使用するクラスがインターフェイスを実装しない場合、すべてがプルに失敗するというエラーが発生しやすくなります。引き金

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

class Warrior
{
    use Triggerable;
}

簡単な解決策は、トレイトを使用するクラスにこれらの関数も実装するように強制することです。

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

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

したがって、トレイトインターフェイスに完全に依存しているわけではありませんが、トレイトを使用するときにクラスが抽象メソッドの実装を要求するため、その関数の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.
    }
}

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.