为什么PHP Trait无法实现接口?

Let*_*eto 79 php oop interface traits

我想知道为什么PHP Trait(PHP 5.4)无法实现接口.

从user1460043的answer => ...更新不能要求使用它来实现特定接口的类

我明白这可能是显而易见的,因为人们可以认为如果一个人Class A正在使用一个Trait T正在实施的interface I,那么Class A应该是直接实现的interface I(这不是真的,因为Class A可以重命名特征方法).

在我的例子中,我的特性是从使用特征的类实现的接口调用方法.

该特征实际上是接口的一些方法的实现.所以,我想在代码中"设计"每个想要使用我的特性的类都必须实现该接口.这将允许Trait使用接口定义的类方法,并确保它们存在于类中.

Dan*_*ack 91

真正的简短版本更简单,因为你不能.这不是Traits的工作方式.

当您use SomeTrait;使用PHP 编写时,您(有效地)告诉编译器将Trait中的代码复制并粘贴到正在使用它的类中.

因为use SomeTrait;它在类中,所以它不能添加implements SomeInterface到类中,因为它必须在类之外.

"为什么不是PHP中的Traits类型?"

因为它们无法实例化.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();
Run Code Online (Sandbox Code Playgroud)

但是 - 如果你确实需要强制使用任何使用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());
}
Run Code Online (Sandbox Code Playgroud)

然而,这仍然不允许您在特征中实现接口,并且仍然闻起来像一个糟糕的设计,因为接口比定义类需要实现的合同的特性要好得多.

  • 我相信PHP核心开发人员应该研究一下Scala,其中特性被认为是完整的类型......我觉得很遗憾PHP逐渐想要改进其打字系统,但没有考虑到现有的良好工作实现 (5认同)
  • 这真的很奇怪,因为实际上我的同事和我只是在我们想要共享实现接口但来自不同祖先的几个类所需的代码时才使用特征.也没有合理的解释为什么编译器可以改变类中的代码而不是这个类实现的接口.......它只是一个"缺失"的功能......"因为你不能"解释这是最好的 (3认同)
  • 那么,您有什么建议呢?我有一个Human类,该类基于Job抽象为子类,但是其中许多Job具有共享功能,最好通过共享代码实现(例如,秘书和程序员都需要`类型`方法)。您能想到没有特质的情况如何实现吗? (2认同)

Ili*_*ija 23

有一个RFC:带有接口的Traits建议将以下内容添加到该语言中:

trait SearchItem implements SearchItemInterface
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

接口所需的方法既可以由trait实现,也可以声明为abstract,在这种情况下,期望使用trait的类实现它.

该语言目前不支持此功能,但它正在考虑之中(RFC的当前状态是:在讨论中).


use*_*043 7

[...]在代码中"设计"每个想要使用我的特征的类都必须实现该接口.这将允许Trait使用接口定义的类方法,并确保它们存在于类中.

这听起来很合理,我不会说你的设计有任何问题.有人提出了这个想法的特点,请看第二点:

  • 特征提供了一组实现行为的方法.
  • 特征需要一组方法作为所提供行为的参数.
  • [...]

Schärli等,Traits:Composable Units of Behavior,ECOOP'2003,LNCS 2743,pp.248-274,Springer Verlag,2003,Page 2

所以说你想要一个特性需要一个接口而不是"实现"它可能更合适.

我没有看到为什么不应该让这个"trait需要(它的消费者类实现)PHP中的接口"功能,但目前它似乎缺失了.

正如@Danack在他的回答中指出的那样,你可以在特征中使用抽象函数来从使用特征的类中"要求"它们.不幸的是,你不能用私人功能这样做.


小智 5

我同意@Danack 的回应,但我会补充一点。

真正简短的版本更简单,因为你做不到。这不是 Traits 的工作原理。

我只能想到少数情况,在这些情况下,您的要求是必要的,并且作为设计问题比作为语言失败更明显。想象一下有一个这样的界面:

interface Weaponize
{
    public function hasAmmunition();
    public function pullTrigger();
    public function fire();
    public function recharge();
}
Run Code Online (Sandbox Code Playgroud)

创建了一个特征,它实现了接口中定义的函数之一,但在此过程中使用了接口也定义的其他函数,容易出现错误:如果使用该功能的类没有实现该接口,则所有内容都无法拉取该功能。扳机

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

class Warrior
{
    use Triggerable;
}
Run Code Online (Sandbox Code Playgroud)

一个简单的解决方案就是强制使用该特征的类也实现这些功能:

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

    public function pullTrigger()
    {
        if ($this->hasAmmunition()) {
            $this->fire();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,特征并不完全依赖于接口而是实现其功能之一的建议,因为当使用特征时时,类将要求实现抽象方法。

最终设计

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

请原谅我的英语