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

时间:2013-02-02 20:08:29

标签: php oop interface traits

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

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

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

就我而言,我的特性是从使用trait的类实现的接口调用方法。

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

4 个答案:

答案 0 :(得分:85)

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

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

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

  

“PHP为什么不是Traits类型?”

因为无法实例化。 Traits实际上只是一个language construct(告诉编译器将特征代码复制并粘贴到这个类中),而不是代码可以引用的对象或类型。

  

所以,我想在代码中“设计”每个想要使用的类   我的特质必须实现界面。

可以使用抽象类强制执行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中定义抽象函数以强制类实现该方法。 e.g。

trait LoggerTrait {

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

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

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

答案 1 :(得分:20)

RFC: Traits with interfaces建议将以下内容添加到语言中:

trait SearchItem implements SearchItemInterface
{
    ...
}

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

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

答案 2 :(得分:7)

  

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

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

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

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

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

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

正如@Danack在他的answer中所说,你可以在特质中使用抽象函数来要求"要求"他们来自使用特征的类。很遗憾,您无法使用private functions执行此操作。

答案 3 :(得分:0)

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

  

真正简短的版本比较简单,因为您做不到。那不是特质的工作方式。

我只能想到在少数情况下您需要的东西是必需的,并且在设计问题上比语言失败更明显,请想象有这样的接口:

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

已经创建了一个特征,该特征实现了界面中定义的功能之一,但在此过程中还使用了由界面定义的其他功能>,容易出错,如果使用该功能的类未实现接口,则所有操作均无法触发触发器

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

因此,特征并不完全依赖于界面,而是一项建议,用于实现其功能之一,因为使用特征该类将要求实现抽象方法。

最终设计

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

对不起,英语